def _assemble_mock_expectations(mock, parse_results, file_name, line_number): mock_path = getattr(parse_results, 'mock_path', None) or None expectations = mock['expectations'].setdefault(mock_path, {'not_called': False, 'calls': []}) if getattr(parse_results, 'not', None): if expectations['calls']: raise FixtureSyntaxError( 'Cannot combine not-called expectations with other expectations for path "{}"'.format(mock_path), file_name, line_number, ) expectations['not_called'] = True else: if expectations['not_called']: raise FixtureSyntaxError( 'Cannot combine not-called expectations with other expectations for path "{}"'.format(mock_path), file_name, line_number, ) value = _get_python_value_from_json(parse_results.json, file_name, line_number) try: if len(value) != 2 or not isinstance(value[0], list) or not isinstance(value[1], dict): raise Exception except Exception: raise FixtureSyntaxError( 'Expected call JSON syntax must be in the form: [[arg1, ...], {"kwarg1": val1, ...}]', file_name, line_number, ) expectations['calls'].append(value)
def _ingest_expect_called(test_target, parse_results, file_name, line_number): key = (parse_results.stub_service, parse_results.stub_action) stub_config = test_target.setdefault('stubbed_actions', {}).setdefault(key, {}) if getattr(parse_results, 'not', False): if 'expect_request' in stub_config: raise FixtureSyntaxError( 'Cannot combine "expect called" and "expect not called" on the same stub action for {}.{}' .format(*key), file_name, line_number, ) stub_config['expect_not_called'] = True else: if 'expect_not_called' in stub_config: raise FixtureSyntaxError( 'Cannot combine "expect called" and "expect not called" on the same stub action for {}.{}' .format(*key), file_name, line_number, ) if getattr(parse_results, 'variable_name', None): path_put( stub_config.setdefault('expect_request', {}), parse_results.variable_name, get_parsed_data_type_value(parse_results, parse_results.value), ) else: stub_config.setdefault('expect_request', {})
def _ingest_error_body(test_target, parse_results, file_name, line_number): key = (parse_results.stub_service, parse_results.stub_action) stub_config = test_target.setdefault('stubbed_actions', {}).setdefault(key, {}) if 'body' in stub_config: raise FixtureSyntaxError( 'Cannot combine stub action body and errors (must choose one) for {}.{}' .format(*key), file_name, line_number, ) field = parse_results.field_name if not field or not field.strip() or field.strip().lower() == 'none': field = None message = parse_results.error_message if not message or not message.strip() or message.strip().lower() == 'none': message = None stub_config.setdefault('errors', []).append({ 'code': parse_results.error_code, 'message': message, 'field': field, })
def ingest_from_parsed_test_fixture(self, action_case, test_case, parse_results, file_name, line_number): if 'description' in test_case: raise FixtureSyntaxError('Duplicate test description directive for test case', file_name, line_number) path_put( test_case, 'description', '{}\n{}'.format(parse_results.description.strip(' \t\n'), file_name), )
def parse_and_store_freeze_to(target, value, file_name, line_number): if not freeze_time: raise FixtureSyntaxError( 'Could not import freezegun to support freeze time syntax. Perhaps you need to install it?', file_name, line_number, ) if value == 'now': freeze_to = None else: try: freeze_to = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') except ValueError: raise FixtureSyntaxError( 'Could not parse datetime value for time freeze', file_name, line_number) target['_freezegun_freeze_time'] = freeze_to
def _ingest_directives(self): # type: () -> None try: assert self._grammar is not None with codecs.open(self._fixture_file_name, mode='rb', encoding='utf-8') as file_input: self._grammar.parseFile(file_input) # explicit finalize on EOF to catch case of no blank line at end of file self._finalize_test_case('', 0, None) except ParseException as e: line_text = get_parse_line(e.loc, e.pstr) line_number = get_parse_line_number(e.loc, e.pstr) offset = get_parse_column(e.loc, e.pstr) raise FixtureSyntaxError( 'Failed to parse line: {line}\nin file: {file}:{line_number}\n{message}' .format( line=line_text, file=self._fixture_file_name, line_number=line_number, message=e.msg, ), file_name=self._fixture_file_name, line_number=line_number, offset=offset, line_text=line_text, ) except DataTypeConversionError as e: raise FixtureSyntaxError( 'Data type conversion error\nin file: {file}:{line_number}\n{message}' .format( file=self._fixture_file_name, line_number=self._line_number, message=e.args[0], ), file_name=self._fixture_file_name, line_number=self._line_number, ) except IOError as e: raise FixtureLoadError(str(e))
def _assemble_mock_path_result(mock, parse_results, file_name, line_number): is_exception = is_delete = None if getattr(parse_results, 'instruction', None): is_exception = parse_results.instruction == 'exception' is_delete = parse_results.instruction == 'delete' mock_path = parse_results.mock_path if is_delete: if mock_path.endswith('return_value') or mock_path.endswith('side_effect'): raise FixtureSyntaxError( 'Cannot delete paths ending in special Mock attributes `return_value` or `side_effect`', file_name, line_number, ) mock['configure'][mock_path] = _DELETE_ATTRIBUTE else: value = parse_results.value if is_exception: try: if ':' not in value: value = '__builtin__:{}'.format(value) if six.PY2 else 'builtins:{}'.format(value) value = fields.PythonPath.resolve_python_path(value) except (ValueError, ImportError, AttributeError) as e: raise FixtureSyntaxError( 'Could not resolve python path for value "{}" due to error: {!r}'.format(value, six.text_type(e)), file_name, line_number, ) else: value = _get_python_value_from_json(value, file_name, line_number) if mock_path.endswith('side_effect'): mock['configure'].setdefault(mock_path, []).append(value) else: mock['configure'][mock_path] = value
def _ingest_action_body(test_target, parse_results, file_name, line_number): key = (parse_results.stub_service, parse_results.stub_action) stub_config = test_target.setdefault('stubbed_actions', {}).setdefault(key, {}) if 'errors' in stub_config: raise FixtureSyntaxError( 'Cannot combine stub action body and errors (must choose one) for {}.{}'.format(*key), file_name, line_number, ) path_put( stub_config.setdefault('body', {}), parse_results.variable_name, get_parsed_data_type_value(parse_results, parse_results.value), )
def ingest_from_parsed_test_fixture(self, action_case, test_case, parse_results, file_name, line_number): if 'name' in test_case: raise FixtureSyntaxError('Duplicate test name directive for test case', file_name, line_number) path_put(test_case, 'name', parse_results.name)
def _finalize_test_case(self, active_string, location, _): # type: (six.text_type, int, Optional[ParseResults]) -> None """ Called by PyParsing at the end of each test case. :param active_string: The contents of the fixture file :param location: The file location of the current parsing activity """ self._fixture_source.append('') if self._working_action_case: # We're done parsing the test case and still need to wrap up the last action in the test case for dc in get_all_directives(): dc().post_parse_test_case_action( self._working_action_case, self._working_test_case or self._global_directives, ) self._working_action_case = {} if not self._working_test_case: # just a blank line before any test cases, probably after globals or an extra blank line between tests return self._working_test_case['line_number'] = self._working_test_case_line_number self._working_test_case_line_number = 0 self._working_test_case['fixture_name'] = self._fixture_name self._working_test_case['fixture_file_name'] = self._fixture_file_name self._working_test_case['source'] = self._working_test_case_source line_number = get_parse_line_number(location, active_string) if not self._working_test_case.get('name'): raise FixtureSyntaxError( '{}:{}: Test case without name'.format(self._fixture_file_name, line_number), file_name=self._fixture_file_name, line_number=line_number - 1, ) if not self._working_test_case.get('description'): raise FixtureSyntaxError( '{}:{}: Test case without description'.format(self._fixture_file_name, line_number), file_name=self._fixture_file_name, line_number=line_number - 1, ) if not self._working_test_case.get('actions') and not self._global_directives: raise FixtureSyntaxError( '{}:{}: Empty test case'.format(self._fixture_file_name, line_number), file_name=self._fixture_file_name, line_number=line_number - 1, ) if self._global_directives: # merge, but make sure current overlays global where there is conflict test_case = {} # type: TestCase for path in get_all_paths(self._global_directives, allow_blank=True): try: value = path_get(self._global_directives, path) path_put(test_case, path, copy.copy(value)) except (KeyError, IndexError): raise FixtureSyntaxError( 'Invalid path: `{}`'.format(path), file_name=self._fixture_file_name, line_number=line_number, ) for path in get_all_paths(self._working_test_case, allow_blank=True): try: path_put(test_case, path, path_get(self._working_test_case, path)) except (KeyError, IndexError): raise FixtureSyntaxError( 'Invalid path: `{}`'.format(path), file_name=self._fixture_file_name, line_number=line_number, ) for directive_class in get_all_directives(): directive_class().post_parse_test_case(test_case) else: for directive_class in get_all_directives(): directive_class().post_parse_test_case(self._working_test_case) test_case = copy.deepcopy(self._working_test_case) test_case['fixture_source'] = self._fixture_source self.test_cases.append(test_case) self._working_test_case.clear() self._working_test_case_source = []
def _get_python_value_from_json(json_value, file_name, line_number): try: return json.loads('{{"value": {}}}'.format(json_value), object_hook=_mock_any_decoder)['value'] except Exception as e: raise FixtureSyntaxError(e.args[0], file_name, line_number)