def process_obj( cls, results: Dict[str, Any], obj: Union[Dict[str, Any], List[Any]], call_results: 'DictObj' = None, ) -> Union[Dict[str, Any], List[Any]]: logger.debug(f'Attempting to process object: {obj}') from utils import extract_attr, generate_attr if type(obj) == dict: obj_iter = obj.keys() elif type(obj) == list: obj_iter = range(len(obj)) for j in obj_iter: if type(obj[j]) == ATTR: obj[j] = generate_attr(attr_type=obj[j]) elif type(obj[j]) in [CALC, CAST, JOIN]: obj[j] = obj[j].execute(scope=results) elif type(obj[j]) == dict: obj[j] = cls.process_obj( results=results, obj=obj[j], call_results=call_results ) elif type(obj[j]) == list: if ( len(obj[j]) and type(obj[j][0]) == dict and '__attr' in obj[j][0].keys() ): if 'count' not in obj[j][0].keys(): obj[j][0]['count'] = 1 obj[j] = [ generate_attr(attr_type=obj[j][0]['__attr'], **obj[j][0]) for ii in range(obj[j][0]['count']) ] else: obj[j] = cls.process_obj( results=results, obj=obj[j], call_results=call_results ) elif type(obj[j]) == str and obj[j].startswith('$__'): if obj[j] == '$__session': obj[j] = cls.session elif obj[j].startswith('$__session.'): obj[j] = extract_attr(scope=cls.session, attr_path=obj[j].replace('session.', '')) else: obj[j] = extract_attr(scope=results, attr_path=obj[j]) elif callable(obj[j]): obj[j] = obj[j](results=results, call_results=call_results) logger.debug(f'Processed object: {obj}') return obj
def execute(self, *, scope: Dict[str, Any]): for i in range(len(self._attrs)): # [DOC] Attempt to extract attr, execute oper if type(self._attrs[i]) == str and self._attrs[i].startswith('$__'): self._attrs[i] = extract_attr(scope=scope, attr_path=self._attrs[i]) elif type(self._attrs[i]) in [CALC, CAST, JOIN]: self._attrs[i] = self._attrs[i].execute(scope=scope) # [DOC] Join using _separator return self._separator.join(self._attrs)
def execute(self, *, scope: Dict[str, Any]): # [DOC] Attempt to extract attr, execute oper if type(self._attr) == str and self._attr.startswith('$__'): self._attr = extract_attr(scope=scope, attr_path=self._attr) elif type(self._attr) in [CALC, CAST, JOIN]: self._attr = self._attr.execute(scope=scope) # [DOC] Casr per _type if self._type == 'int': return int(self._attr) elif self._type == 'float': return float(self._attr) elif self._type == 'str': return str(self._attr)
def execute(self, *, scope: Dict[str, Any]): results = None for i in range(len(self._attrs)): # [DOC] Attempt to extract attr, execute oper if type(self._attrs[i]) == str and self._attrs[i].startswith('$__'): self._attrs[i] = extract_attr(scope=scope, attr_path=self._attrs[i]) elif type(self._attrs[i]) in [CALC, CAST, JOIN]: self._attrs[i] = self._attrs[i].execute(scope=scope) # [DOC] Calculate results per _oper if i == 0: results = self._attrs[i] else: results = getattr((results if results else 0), self._opers[self._oper])( self._attrs[i] ) return results
async def run_call( cls, results: Dict[str, Any], module: str, method: str, skip_events: List[Event], query: List[Any], doc: LIMP_DOC, acceptance: Dict[str, Any], ): from config import Config call_results = { 'step': 'call', 'module': module, 'method': method, 'query': query, 'doc': doc, 'status': True, } query = Query(cls.process_obj(results=results, obj=query)) doc = cls.process_obj(results=results, obj=doc) try: call_results['results'] = await Config.modules[module].methods[method]( skip_events=skip_events, env={**cls.env, 'session': cls.session}, query=query, doc=doc, call_id='__TEST__', ) call_results['acceptance'] = cls.process_obj( results=results, obj=copy.deepcopy(acceptance), call_results=call_results, ) for measure in acceptance.keys(): if measure.startswith('session.'): results_measure = extract_attr( scope=cls.session, attr_path=f'$__{measure.replace("session.", "")}' ) else: results_measure = extract_attr( scope=call_results['results'], attr_path=f'$__{measure}' ) if results_measure != call_results['acceptance'][measure]: call_results['status'] = False cls.break_debugger(locals(), call_results) break if call_results['status'] == False: logger.debug( f'Test step \'call\' failed at measure \'{measure}\'. Required value is \'{call_results["acceptance"][measure]}\', but test results is \'{results_measure}\'' ) call_results['measure'] = measure except Exception as e: tb = traceback.format_exc() logger.error(f'Exception occured: {tb}') cls.break_debugger(locals(), call_results) call_results.update( { 'measure': measure, 'results': { 'status': 500, 'msg': str(e), 'args': {'tb': tb, 'code': 'SERVER_ERROR'}, }, } ) call_results['status'] = False call_results['measure'] = measure if call_results['status'] == True and 'session' in call_results['results'].args: call_results['session'] = call_results['results'].args.session return call_results
async def run_test( cls, test_name: str, steps: List[STEP] = None ) -> Union[None, Dict[str, Any]]: from config import Config from utils import DictObj if test_name not in Config.tests.keys(): logger.error('Specified test is not defined in loaded config.') logger.debug(f'Loaded tests: {list(Config.tests.keys())}') exit() test: List[STEP] = Config.tests[test_name] results = { 'test': Config.tests[test_name], 'status': 'PASSED', 'success_rate': 100, 'stats': {'passed': 0, 'failed': 0, 'skipped': 0, 'total': 0}, 'steps': [], } step_failed = False for i in range(len(test)): results['stats']['total'] += 1 step = copy.deepcopy(test[i]) if steps and i not in steps: results['stats']['total'] -= 1 results['stats']['skipped'] += 1 continue if step_failed and not Config.test_force: results['stats']['skipped'] += 1 continue if step._step == 'AUTH': try: STEP.validate_step(step=step) step = STEP.CALL( module='session', method='auth', doc={ step._args['var']: step._args['val'], 'hash': step._args['hash'], }, ) except InvalidTestStepException as e: logger.error( f'Can\'t process test step \'AUTH\' with error: {e} Exiting.' ) exit() elif step._step == 'SIGNOUT': step = STEP.CALL( module='session', method='signout', query=[{'_id': '$__session'}] ) else: try: STEP.validate_step(step=step) except InvalidTestStepException as e: logger.error(f'{e} Exiting.') exit() if step._step == 'CALL': call_results = await cls.run_call(results=results, **step._args) if 'session' in call_results.keys(): logger.debug( 'Updating session after detecting \'session\' in call results.' ) if str(call_results['session']._id) == 'f00000000000000000000012': cls.session = DictObj( { **Config.compile_anon_session(), 'user': DictObj(Config.compile_anon_user()), } ) else: cls.session = call_results['session'] results['steps'].append(call_results) elif step._step == 'TEST': test_results = await cls.run_test( test_name=step._args['test'], steps=step._args['steps'] ) if test_results['status'] == 'PASSED': test_results['status'] = True else: test_results['status'] = False results['steps'].append(test_results) elif step._step == 'SET_REALM': try: if step._args['realm'].startswith('$__'): step._args['realm'] = extract_attr( scope=results, attr_path=step._args['realm'] ) logger.debug(f'Updating realm to \'{step._args["realm"]}\'.') cls.env['realm'] = step._args['realm'] results['steps'].append( { 'step': 'set_realm', 'realm': step._args['realm'], 'status': True, } ) except Exception as e: logger.error(e) results['steps'].append( { 'step': 'set_realm', 'realm': step._args['realm'], 'status': False, } ) if not results['steps'][-1]['status']: results['stats']['failed'] += 1 if not Config.test_force: step_failed = True else: results['stats']['passed'] += 1 if len(results['steps']) == 0: logger.error('No steps tested. Exiting.') exit() results['success_rate'] = int( (results['stats']['passed'] / results['stats']['total']) * 100 ) if results['success_rate'] == 0: results['status'] = 'FAILED' elif results['success_rate'] == 100: results['status'] = 'PASSED' else: results['status'] = 'PARTIAL' if test_name == Config.test: logger.debug( 'Finished testing %s steps [Passed: %s, Failed: %s, Skipped: %s] with success rate of: %s%%', results['stats']['total'], results['stats']['passed'], results['stats']['failed'], results['stats']['skipped'], results['success_rate'], ) tests_log = os.path.join( Config._limp_location, 'tests', f'LIMP-TEST_{test_name}_{datetime.datetime.utcnow().strftime("%d-%b-%Y")}', ) if os.path.exists(f'{tests_log}.json'): i = 1 while True: if os.path.exists(f'{tests_log}.{i}.json'): i += 1 else: tests_log = f'{tests_log}.{i}' break tests_log += '.json' with open(tests_log, 'w') as f: f.write(json.dumps(json.loads(JSONEncoder().encode(results)), indent=4)) logger.debug(f'Full tests log available at: {tests_log}') else: return results
async def _extend_doc( cls, *, env: Dict[str, Any], doc: LIMP_DOC, attr: Union[None, LIMP_DOC], extn_id: ObjectId, extn: EXTN, extn_models: Dict[str, BaseModel] = {}, ) -> BaseModel: # [DOC] Check if extn module is dynamic value if extn.module.startswith('$__'): extn_module = Config.modules[extract_attr(scope={ 'doc': doc, 'attr': attr }, attr_path=extn.module)] else: extn_module = Config.modules[extn.module] # [DOC] Check if extn attr set to fetch all or specific attrs if type(extn.attrs) == str and extn.attrs.startswith('$__'): extn_attrs = extract_attr(scope={ 'doc': doc, 'attr': attr }, attr_path=extn.attrs) if extn_attrs[0] == '*': extn_attrs = { attr: extn_module.attrs[attr] for attr in extn_module.attrs.keys() } elif extn.attrs[0] == '*': extn_attrs = { attr: extn_module.attrs[attr] for attr in extn_module.attrs.keys() } else: extn_attrs = {attr: extn_module.attrs[attr] for attr in extn.attrs} # [DOC] Implicitly add _id key to extn attrs so that we don't delete it in process extn_attrs['_id'] = 'id' # [DOC] Set skip events skip_events = [Event.PERM] # [DOC] Check if extn instruction is explicitly requires second-dimension extn. if extn.force == False: skip_events.append(Event.EXTN) elif type(extn.force) == str and extn.force.startswith('$__'): if not extract_attr(scope={ 'doc': doc, 'attr': attr }, attr_path=extn.attrs): skip_events.append(Event.EXTN) # [DOC] Read doc if not in extn_models if str(extn_id) not in extn_models.keys(): extn_results = await extn_module.methods['read']( skip_events=skip_events, env=env, query=[{ '_id': extn_id }]) if extn_results['args']['count']: extn_models[str(extn_id)] = extn_results['args']['docs'][0] else: extn_models[str(extn_id)] = None # [DOC] Set attr to extn_models doc extn_doc = copy.deepcopy(extn_models[str(extn_id)]) # [DOC] delete all unneeded keys from the resulted doc if extn_doc: extn_doc = BaseModel({ attr: extn_doc[attr] for attr in extn_attrs.keys() if attr in extn_doc }) return extn_doc
def _parse_permission_args( self, skip_events: List[str], env: Dict[str, Any], query: Union[LIMP_QUERY, Query], doc: LIMP_DOC, permission_args: Any, ): user = env['session'].user if type(permission_args) == list: args_iter = range(len(permission_args)) elif type(permission_args) == dict: args_iter = list(permission_args.keys()) for j in args_iter: if type(permission_args[j]) == ATTR_MOD: # [DOC] If attr is of type ATTR_MOD, call condition callable if permission_args[j].condition(skip_events=skip_events, env=env, query=query, doc=doc): # [DOC] If condition return is True, update attr value if callable(permission_args[j].default): permission_args[j] = permission_args[j].default( skip_events=skip_events, env=env, query=query, doc=doc) if type(permission_args[j]) == Exception: raise permission_args[j] else: permission_args[j] = permission_args[j].default elif type(permission_args[j]) == dict: # [DOC] Check opers for oper in [ '$gt', '$lt', '$gte', '$lte', '$bet', '$ne', '$regex', '$all', '$in', ]: if oper in permission_args[j].keys(): if oper == '$bet': permission_args[j][ '$bet'] = self._parse_permission_args( skip_events=skip_events, env=env, query=query, doc=doc, permission_args=permission_args[j]['$bet'], ) else: permission_args[j][ oper] = self._parse_permission_args( skip_events=skip_events, env=env, query=query, doc=doc, permission_args=[permission_args[j][oper]], )[0] # [DOC] Continue the iteration continue # [DOC] Child args, parse permission_args[j] = self._parse_permission_args( skip_events=skip_events, env=env, query=query, doc=doc, permission_args=permission_args[j], ) elif type(permission_args[j]) == list: permission_args[j] = self._parse_permission_args( skip_events=skip_events, env=env, query=query, doc=doc, permission_args=permission_args[j], ) elif type(permission_args[j]) == str: # [DOC] Check for variables if permission_args[j] == '$__user': permission_args[j] = user._id elif permission_args[j].startswith('$__user.'): permission_args[j] = extract_attr( scope=user, attr_path=permission_args[j].replace( '$__user.', '$__'), ) elif permission_args[j] == '$__access': permission_args[j] = { '$__user': user._id, '$__groups': user.groups } elif permission_args[j] == '$__datetime': permission_args[j] = datetime.datetime.utcnow().isoformat() elif permission_args[j] == '$__date': permission_args[j] = datetime.date.today().isoformat() elif permission_args[j] == '$__time': permission_args[j] = datetime.datetime.now().time( ).isoformat() return permission_args