def _validate_additional_property(self, property_name, property_value, resource_path, unused_properties): if self.allow_additional_properties: print "DEBUG::Additional property {resource_path}->{property_name}".format( resource_path=resource_path, property_name=property_name) try_suggest_other_property_names(name=property_name, candidates=unused_properties) odata_type = match_dynamic_property(property_name, self) if odata_type is None: try: odata_type = get_odata_type(property_value) except KeyError: if isinstance(property_value, dict): cts_error("Property {path}->{property_name} is of complex type without @odata_type", path=resource_path, property_name=property_name) return ValidationStatus.FAILED else: return ValidationStatus.PASSED mapped_odata_type = self.metadata_container.map_type(odata_type) # assuming all aditional properties are nullable if property_value is None: return ValidationStatus.PASSED if not self.metadata_container.to_be_ignored(odata_type, mapped_odata_type): return self.metadata_container.types[odata_type].validate( property_value, resource_path + '->' + property_name) else: return ValidationStatus.PASSED else: cts_error("Property {resource_path}->{property_name} is present, but not defined in metadata. " \ "Type {type_name} does not allow additional properties", resource_path=resource_path, property_name=property_name, type_name=self.name) try_suggest_other_property_names(name=property_name, candidates=unused_properties) return ValidationStatus.FAILED
def _validate_additional_property(self, property_name, property_value, resource_path, unused_properties): if self.allow_additional_properties: print "DEBUG::Additional property {odata_id}->{property_name}" \ .format(odata_id=resource_path, property_name=property_name) try_suggest_other_property_names(name=property_name, candidates=unused_properties) odata_type = match_dynamic_property(property_name, self) if odata_type is None: try: odata_type = get_odata_type(property_value) except KeyError: if isinstance(property_value, dict): cts_error("Property {odata_id:id}->{key} is of complex type without @odata_type", odata_id=resource_path, key=property_name) return ValidationStatus.FAILED else: return ValidationStatus.PASSED mapped_odata_type = self.metadata_container.map_type(odata_type) if not self.metadata_container.to_be_ignored(odata_type, mapped_odata_type): return self.metadata_container.types[mapped_odata_type].validate( property_value, resource_path + '->' + property_name) else: cts_error("Property {odata_id:id}->{property_name} is present, but not defined in metadata. " "Entity {entity_name} does not allow additional properties", odata_id=resource_path, property_name=property_name, entity_name=self.name) try_suggest_other_property_names(name=property_name, candidates=unused_properties) status = ValidationStatus.FAILED
def validate(self, resource, resource_path, annotations=None): """ :type annotations: dict :type resource: dict :type resource_path: str :rtype: str """ try: odata_type = self.metadata_container.map_type(get_odata_type(resource)) mapped_odata_type = self.metadata_container.map_type(odata_type) if self.metadata_container.to_be_ignored(odata_type, mapped_odata_type): return ValidationStatus.PASSED except KeyError: pass #this is legal that complex type does not have odata.type specified types_consistency_validation_status, complex_type_name = self.validate_types_consistency(resource, resource_path) if self.metadata_container.to_be_ignored(complex_type_name): return ValidationStatus.PASSED if complex_type_name == self.name: resource_validation_status = self.validate_properties(resource, resource_path) else: try: resource_validation_status = self.metadata_container.types[complex_type_name].validate(resource, resource_path) except KeyError: resource_validation_status = ValidationStatus.FAILED except Exception as err: import traceback cts_error("{path} - exception {err:exceptions}: {stack:stacktrace}", path=resource_path, err=err, stack=traceback.format_exc()) resource_validation_status = ValidationStatus.FAILED return ValidationStatus.join_statuses(types_consistency_validation_status, resource_validation_status)
def _validate_types_consistency(self, resource, resource_path, odata_type=None): """ :type resource: dict :type resource_path: str :type odata_type: str :return: validation_status, type that will be validated :rtype: str, str """ if not odata_type: try: odata_type = get_odata_type(resource) except KeyError: odata_type = None if not odata_type: cts_warning("Missing odata.type property in {odata_id:id}", odata_id=resource_path) return ValidationStatus.PASSED_WITH_WARNINGS, self.name else: reported_odata_type = self.metadata_container.map_type(odata_type) if self.metadata_container.to_be_ignored(odata_type, reported_odata_type): return ValidationStatus.PASSED, reported_odata_type try: if self.is_compatible(reported_odata_type): msg = ( "DEBUG::Resource {odata_id} reported entity type {reported_odata_type}, " + "expected type {expected_entity_type}, types are consistent" ).format(odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name) print msg return ValidationStatus.PASSED, reported_odata_type else: cts_error( "Resource {odata_id:id} reported entity type {reported_odata_type}, " + "expected type {expected_entity_type}, types are not consistent", odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name) return ValidationStatus.FAILED, reported_odata_type except KeyError: cts_error( "Resource {odata_id:id} reported unknown entity type {reported_odata_type}," + "will process validation with expected entity type {expected_entity_type}", odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name) return ValidationStatus.FAILED, reported_odata_type
def _process_complex_type(self, property_description, body, url, path): try: # use override @odata.type if present. # At this stage do not verify if override type is consistent with type from property_description; # This will be validated in validate_get_responses test odata_type = get_odata_type(body) except KeyError: odata_type = property_description.type type_definition = self._metadata_container.types[odata_type] self._process_properties(body, type_definition, url, path)
def _validate_property_list(self, context, variable_path, property_list): """ :type context: Context :type variable_path: list [str or int] :type property_list: list[cts_core.metadata.model.property.Property] :rtype: str """ api_resource = context.api_resource status = ValidationStatus.PASSED # use local copy - api resource will be modified while test is executed local_property_list = list(property_list) for property_description in local_property_list: status = ValidationStatus.join_statuses( self._validate_property(context, variable_path, property_description), status) # validate additional properties try: body = api_resource.get_value_from_path(variable_path) except KeyError as key: cts_warning("Unable to access {odataid:id}->{path}. Key={key}", odataid=api_resource.odata_id, path="->".join(variable_path), key=key) body = None if body is not None: all_properties = set(body.keys()) known_properties = set( [property.name for property in local_property_list]) additional_properties = all_properties - known_properties for additional_property_name in additional_properties: property_value = body[additional_property_name] if isinstance(property_value, dict) and "@odata.type" in property_value: raw_soup = """ <Property Name="{name}" Type="{type}"> <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/> </Property> """ soup = BeautifulSoup( raw_soup.format(name=additional_property_name, type=get_odata_type(property_value)), "lxml").find_all("property")[0] adhoc_description = Property(self._metadata_container, None, soup) status = ValidationStatus.join_statuses( self._validate_property(context, variable_path, adhoc_description), status) return status
def validate(self, resource, resource_path, annotations=None, odata_type=None): """ :type annotations: dict :type resource: dict :type resource_path: str :type odata_type: str :rtype: str """ if not odata_type: try: odata_type = get_odata_type(resource) except KeyError: odata_type = None if not odata_type: cts_warning( "Resource {odata_id} did not report odata.type property", odata_id=resource_path) return ValidationStatus.PASSED_WITH_WARNINGS else: mapped_odata_type = self.metadata_container.map_type(odata_type) if self.metadata_container.to_be_ignored(odata_type, mapped_odata_type): return ValidationStatus.PASSED types_consistency_validation_status, entity_type = self._validate_types_consistency( resource, resource_path, odata_type) if entity_type == self.name: resource_validation_status = self._validate_container( resource, resource_path) else: try: resource_validation_status = self.metadata_container.entities[ entity_type]._validate_container(resource, resource_path) except KeyError: resource_validation_status = ValidationStatus.FAILED except Exception as error: cts_error("{id:id} Unexpected error: {error:exception}", id == resource_path, error=error) resource_validation_status = ValidationStatus.FAILED return ValidationStatus.join_statuses( types_consistency_validation_status, resource_validation_status)
def validate_types_consistency(self, container, path): """ :type container: dict :type path: str :return: validation_status, type that will be validated :rtype: str, str """ try: odata_type = get_odata_type(container) except KeyError: odata_type = self.name reported_odata_type = self.metadata_container.map_type(odata_type) if self.metadata_container.to_be_ignored(odata_type, reported_odata_type): return ValidationStatus.PASSED, reported_odata_type if reported_odata_type == self.name: return ValidationStatus.PASSED, self.name try: if self.is_compatible(reported_odata_type): msg = ("DEBUG::Resource {resource_path} reported type {reported_odata_type}, " + "expected type {expected_type}, types are consistent").format( resource_path=path, reported_odata_type=reported_odata_type, expected_type=self.name) print msg return ValidationStatus.PASSED, reported_odata_type else: cts_error("Resource {resource_path} reported type {reported_odata_type}, " + "expected type {expected_type}, types are not consistent", resource_path=path, reported_odata_type=reported_odata_type, expected_type=self.name) print "MESSAGE::Setting results to ERROR, but will process validation with reported complex type " + \ "{reported_odata_type}".format(reported_odata_type=reported_odata_type) return ValidationStatus.FAILED, reported_odata_type except KeyError: cts_error("Resource {resource_path} reported unknown type {reported_odata_type}," + "will process validation with expected entity type {expected_type}", resource_path=path, reported_odata_type=reported_odata_type, expected_type=self.name) return ValidationStatus.FAILED, self.name except AttributeError: cts_error("Resource {resource_path} reported itself as not complex type {reported_type}", resource_path=path, reported_type=reported_odata_type) return ValidationStatus.FAILED, self.name
def _validate_types_consistency(self, resource, resource_path, odata_type=None): """ :type resource: dict :type resource_path: str :type odata_type: str :return: validation_status, type that will be validated :rtype: str, str """ if not odata_type: try: odata_type = get_odata_type(resource) except KeyError: odata_type = None if not odata_type: cts_warning("Missing odata.type property in {odata_id:id}", odata_id=resource_path) return ValidationStatus.PASSED_WITH_WARNINGS, self.name else: reported_odata_type = self.metadata_container.map_type(odata_type) if self.metadata_container.to_be_ignored(odata_type, reported_odata_type): return ValidationStatus.PASSED, reported_odata_type try: if self.is_compatible(reported_odata_type): msg = ("DEBUG::Resource {odata_id} reported entity type {reported_odata_type}, " + "expected type {expected_entity_type}, types are consistent").format( odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name) print msg return ValidationStatus.PASSED, reported_odata_type else: cts_error("Resource {odata_id:id} reported entity type {reported_odata_type}, " + "expected type {expected_entity_type}, types are not consistent", odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name) return ValidationStatus.FAILED, reported_odata_type except KeyError: cts_error("Resource {odata_id:id} reported unknown entity type {reported_odata_type}," + "will process validation with expected entity type {expected_entity_type}", odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name) return ValidationStatus.FAILED, reported_odata_type
def _append_additional_properties(self, property_list, body, type_definition, url, path): additionals = set(body.keys()) - set( [property.name for property in property_list]) # filter out special properties (properties that contain @ and #) additionals = filter( lambda property: not is_special_property(property), additionals) if len(additionals) > 0: if type_definition.allow_additional_properties: for property_name in additionals: # process only objects try: property_value = body[property_name] if isinstance(property_value, dict): odata_type = match_dynamic_property( property_name, type_definition) if odata_type is None: try: odata_type = get_odata_type(property_value) except KeyError as key: cts_error( "{url:id}#{path}: @odata.type not found in complex " "additional property", url=url, path=path.append(property_name)) continue adhoc_description = self.ad_hoc_type_definition( property_name, odata_type) property_list.append(adhoc_description) except KeyError: pass else: cts_error( "{url:id}#{path}: Object of type {type}; unexpected properties: [{" "properties}]", type=type_definition.name, properties=", ".join(additionals), url=url, path=path) self._status = ValidationStatus.FAILED
def validate(self, resource, resource_path, annotations=None, odata_type=None): """ :type annotations: dict :type resource: dict :type resource_path: str :type odata_type: str :rtype: str """ if not odata_type: try: odata_type = get_odata_type(resource) except KeyError: odata_type = None if not odata_type: cts_warning("Resource {odata_id} did not report odata.type property", odata_id=resource_path) return ValidationStatus.PASSED_WITH_WARNINGS else: mapped_odata_type = self.metadata_container.map_type(odata_type) if self.metadata_container.to_be_ignored(odata_type, mapped_odata_type): return ValidationStatus.PASSED types_consistency_validation_status, entity_type = self._validate_types_consistency(resource, resource_path, odata_type) if entity_type == self.name: resource_validation_status = self._validate_container(resource, resource_path) else: try: resource_validation_status = self.metadata_container.entities[entity_type]._validate_container(resource, resource_path) except KeyError: resource_validation_status = ValidationStatus.FAILED except Exception as error: cts_error("{id:id} Unexpected error: {error:exception}", id==resource_path, error=error) resource_validation_status = ValidationStatus.FAILED return ValidationStatus.join_statuses(types_consistency_validation_status, resource_validation_status)