class TestCentralRoleProvider: _roles = [ central_models.RolePreview(role) for role in load_json(FileNames.central_role_file) ] @mock.patch("azext_iot.central.services.role") def test_should_return_roles(self, mock_role_svc): # setup provider = CentralRoleProviderPreview(cmd=None, app_id=app_id) mock_role_svc.list_roles.return_value = self._roles # act roles = provider.list_roles() # verify # call counts should be at most 1 since the provider has a cache assert mock_role_svc.list_roles.call_count == 1 assert set(roles) == set(map(lambda x: x.id, self._roles)) @mock.patch("azext_iot.central.services.role") def test_should_return_role(self, mock_role_svc): # setup provider = CentralRoleProviderPreview(cmd=None, app_id=app_id) mock_role_svc.get_role.return_value = self._roles[0] # act role = provider.get_role(self._roles[0].id) # verify # call counts should be at most 1 since the provider has a cache assert mock_role_svc.get_role.call_count == 1 assert role.id == self._roles[0].id
class TestCentralDeviceProvider: _device = load_json(FileNames.central_device_file) _device_template = load_json(FileNames.central_device_template_file) @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_should_return_device(self, mock_device_svc, mock_device_template_svc): # setup provider = CentralDeviceProvider(cmd=None, app_id=app_id) mock_device_svc.get_device.return_value = self._device mock_device_template_svc.get_device_template.return_value = ( self._device_template ) # act device = provider.get_device("someDeviceId") # check that caching is working device = provider.get_device("someDeviceId") # verify # call counts should be at most 1 since the provider has a cache assert mock_device_svc.get_device.call_count == 1 assert mock_device_svc.get_device_template.call_count == 0 assert device == self._device @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_should_return_device_template( self, mock_device_svc, mock_device_template_svc ): # setup provider = CentralDeviceTemplateProvider(cmd=None, app_id=app_id) mock_device_svc.get_device.return_value = self._device mock_device_template_svc.get_device_template.return_value = ( self._device_template ) # act template = provider.get_device_template("someDeviceTemplate") # check that caching is working template = provider.get_device_template("someDeviceTemplate") # verify # call counts should be at most 1 since the provider has a cache assert mock_device_template_svc.get_device_template.call_count == 1 assert template == self._device_template
def test_template_component_list(self): expected_component_list = [ "_rpgcmdpo", "RS40OccupancySensorV36fy", ] template = Template( load_json(FileNames.central_property_validation_template_file)) assert collections.Counter( template.components.keys()) == collections.Counter( expected_component_list)
def test_extract_schema_type_component(self): expected_mapping = { "component1Prop": "boolean", "testComponent": "boolean", "component1PropReadonly": "boolean", "component1Prop2": "boolean", } template = Template( load_json(FileNames.central_property_validation_template_file)) for key, val in expected_mapping.items(): schema = template.get_schema(key, is_component=True) schema_type = extract_schema_type(schema) assert schema_type == val
def test_template_interface_list(self): expected_interface_list = [ "urn:sampleApp:groupOne_bz:_rpgcmdpo:1", "urn:sampleApp:groupTwo_bz:myxqftpsr:2", "urn:sampleApp:groupThree_bz:myxqftpsr:2", "urn:sampleApp:groupOne_bz:2", ] template = Template( load_json(FileNames.central_property_validation_template_file)) assert collections.Counter( template.interfaces.keys()) == collections.Counter( expected_interface_list)
def test_validate_invalid_telmetry_component_template_should_fail(self): # setup device_template = central_models.TemplateV1( load_json(FileNames.central_property_validation_template_file)) properties = MessageProperties(content_encoding=self.encoding, content_type=self.content_type) message = Message( body=json.dumps(self.bad_dcm_payload).encode(), properties=properties, annotations={ common_parser.DEVICE_ID_IDENTIFIER: self.device_id.encode(), common_parser.COMPONENT_NAME_IDENTIFIER: list(device_template.components.keys())[1].encode(), }, application_properties=_encode_app_props(self.app_properties), ) args = CommonParserArguments(properties=["all"]) parser = self._create_parser(device_template=device_template, message=message, args=args) # act parsed_msg = parser.parse_message() # verify assert parsed_msg["event"]["payload"] == self.bad_dcm_payload assert parsed_msg["event"]["origin"] == self.device_id device_identifier = str(common_parser.DEVICE_ID_IDENTIFIER, "utf8") assert parsed_msg["event"]["annotations"][ device_identifier] == self.device_id component_identifier = str(common_parser.COMPONENT_NAME_IDENTIFIER, "utf8") assert ( parsed_msg["event"]["annotations"][component_identifier] == list( device_template.components.keys())[1]) properties = parsed_msg["event"]["properties"] assert properties["system"]["content_encoding"] == self.encoding assert properties["system"]["content_type"] == self.content_type assert properties["application"] == self.app_properties expected_details = strings.invalid_field_name_component_mismatch_template( list(self.bad_dcm_payload.keys()), device_template.component_schema_names, ) _validate_issues(parser, Severity.warning, 1, 1, [expected_details])
def test_extract_schema_type_component_identifier(self): expected_mapping = { "component2prop": "boolean", "component2Prop2": "boolean", "testComponent": "boolean", "component2PropReadonly": "boolean", } template = central_models.TemplateV1( load_json(FileNames.central_property_validation_template_file) ) for key, val in expected_mapping.items(): schema = template.get_schema( key, is_component=True, identifier="RS40OccupancySensorV36fy" ) schema_type = extract_schema_type(schema) assert schema_type == val
class TestCentralDeviceGroupProvider: _device_groups = [ central_models.DeviceGroupPreview(group) for group in load_json(FileNames.central_device_group_file) ] @mock.patch("azext_iot.central.services.device_group") def test_should_return_device_groups(self, mock_device_group_svc): # setup provider = CentralDeviceGroupProviderPreview(cmd=None, app_id=app_id) mock_device_group_svc.list_device_groups.return_value = self._device_groups # act device_groups = provider.list_device_groups() # verify # call counts should be at most 1 since the provider has a cache assert mock_device_group_svc.list_device_groups.call_count == 1 assert set(device_groups) == set( map(lambda x: x.id, self._device_groups))
def test_extract_schema_type(self): expected_mapping = { "Bool": "boolean", "Date": "date", "DateTime": "dateTime", "Double": "double", "Duration": "duration", "IntEnum": "Enum", "StringEnum": "Enum", "Float": "float", "Geopoint": "geopoint", "Long": "long", "Object": "Object", "String": "string", "Time": "time", "Vector": "vector", } template = Template(load_json(FileNames.central_device_template_file)) for key, val in expected_mapping.items(): schema = template.get_schema(key) schema_type = extract_schema_type(schema) assert schema_type == val
def test_object_deep(self, value, expected_result): template = Template( load_json(FileNames.central_deeply_nested_device_template_file)) schema = template.get_schema("RidiculousObject") assert validate(schema, value) == expected_result
def test_str_enum(self, value, expected_result): template = Template(load_json(FileNames.central_device_template_file)) schema = template.get_schema("StringEnum") assert validate(schema, value) == expected_result
class TestCentralPropertyMonitor: _device_twin = load_json(FileNames.central_device_twin_file) _duplicate_property_template = load_json( FileNames.central_property_validation_template_file ) @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_should_return_updated_properties( self, mock_device_svc, mock_device_template_svc ): # setup device_twin_data = json.dumps(self._device_twin) raw_twin = ast.literal_eval( device_twin_data.replace("current_time", datetime.now().isoformat()) ) twin = DeviceTwin(raw_twin) twin_next = DeviceTwin(raw_twin) twin_next.reported_property.version = twin.reported_property.version + 1 monitor = PropertyMonitor( cmd=None, app_id=app_id, device_id=device_id, token=None, central_dns_suffix=None, ) result = monitor._compare_properties( twin_next.reported_property, twin.reported_property ) assert len(result) == 3 assert len(result["$iotin:urn_azureiot_Client_SDKInformation"]) == 3 assert result["$iotin:urn_azureiot_Client_SDKInformation"]["language"] assert result["$iotin:urn_azureiot_Client_SDKInformation"]["version"] assert result["$iotin:urn_azureiot_Client_SDKInformation"]["vendor"] assert len(result["$iotin:deviceinfo"]) == 8 assert result["$iotin:deviceinfo"]["manufacturer"] assert result["$iotin:deviceinfo"]["model"] assert result["$iotin:deviceinfo"]["osName"] assert result["$iotin:deviceinfo"]["processorArchitecture"] assert result["$iotin:deviceinfo"]["swVersion"] assert result["$iotin:deviceinfo"]["processorManufacturer"] assert result["$iotin:deviceinfo"]["totalStorage"] assert result["$iotin:deviceinfo"]["totalMemory"] assert len(result["$iotin:settings"]) == 1 assert result["$iotin:settings"]["fanSpeed"] @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_should_return_no_properties( self, mock_device_svc, mock_device_template_svc ): # test to check that no property updates are reported when version is not upadted # setup device_twin_data = json.dumps(self._device_twin) raw_twin = ast.literal_eval( device_twin_data.replace("current_time", datetime.now().isoformat()) ) twin = DeviceTwin(raw_twin) twin_next = DeviceTwin(raw_twin) monitor = PropertyMonitor( cmd=None, app_id=app_id, device_id=device_id, token=None, central_dns_suffix=None, ) result = monitor._compare_properties( twin_next.reported_property, twin.reported_property ) assert result is None @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_validate_properties_declared_multiple_interfaces( self, mock_device_svc, mock_device_template_svc ): # setup mock_device_template_svc.get_device_template.return_value = Template( self._duplicate_property_template ) monitor = PropertyMonitor( cmd=None, app_id=app_id, device_id=device_id, token=None, central_dns_suffix=None, ) model = {"Model": "test_model"} issues = monitor._validate_payload_against_entities( model, list(model.keys())[0], Severity.warning, ) assert ( issues[0].details == "Duplicate property: 'Model' found under following " "interfaces ['urn:sampleApp:groupOne_bz:_rpgcmdpo:1', 'urn:sampleApp:groupTwo_bz:myxqftpsr:2', " "'urn:sampleApp:groupThree_bz:myxqftpsr:2'] " "in the device model. Either provide the interface name as part " "of the device payload or make the propery name unique in the device model" ) version = {"OsName": "test_osName"} issues = monitor._validate_payload_against_entities( version, list(version.keys())[0], Severity.warning, ) assert len(issues) == 0 @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_validate_properties_name_miss_under_interface( self, mock_device_svc, mock_device_template_svc ): # setup mock_device_template_svc.get_device_template.return_value = Template( self._duplicate_property_template ) monitor = PropertyMonitor( cmd=None, app_id=app_id, device_id=device_id, token=None, central_dns_suffix=None, ) # invalid interface / property definition = {"definition": "test_definition"} issues = monitor._validate_payload_against_entities( definition, list(definition.keys())[0], Severity.warning, ) assert ( issues[0].details == "Device is sending data that has not been defined in the device template." " Following capabilities have NOT been defined in the device template '['definition']'." " Following capabilities have been defined in the device template (grouped by interface)" " '{'urn:sampleApp:groupOne_bz:2': ['addRootProperty', 'addRootPropertyReadOnly', 'addRootProperty2']," " 'urn:sampleApp:groupOne_bz:_rpgcmdpo:1': ['Model', 'Version', 'TotalStorage']," " 'urn:sampleApp:groupTwo_bz:myxqftpsr:2': ['Model', 'Manufacturer']," " 'urn:sampleApp:groupThree_bz:myxqftpsr:2': ['Manufacturer', 'Version', 'Model', 'OsName']}'. " ) @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_validate_properties_severity_level( self, mock_device_svc, mock_device_template_svc ): # setup mock_device_template_svc.get_device_template.return_value = Template( self._duplicate_property_template ) monitor = PropertyMonitor( cmd=None, app_id=app_id, device_id=device_id, token=None, central_dns_suffix=None, ) # severity level info definition = {"definition": "test_definition"} issues = monitor._validate_payload_against_entities( definition, list(definition.keys())[0], Severity.info, ) assert ( issues[0].details == "Device is sending data that has not been defined in the device template. " "Following capabilities have NOT been defined in the device template " "'['definition']'. Following capabilities have been defined in the device template " "(grouped by interface) '{'urn:sampleApp:groupOne_bz:2': " "['addRootProperty', 'addRootPropertyReadOnly', 'addRootProperty2'], " "'urn:sampleApp:groupOne_bz:_rpgcmdpo:1': ['Model', 'Version', 'TotalStorage'], " "'urn:sampleApp:groupTwo_bz:myxqftpsr:2': ['Model', 'Manufacturer'], " "'urn:sampleApp:groupThree_bz:myxqftpsr:2': ['Manufacturer', 'Version', 'Model', 'OsName']}'. " ) # severity level error issues = monitor._validate_payload_against_entities( definition, list(definition.keys())[0], Severity.error, ) assert len(issues) == 0 @mock.patch("azext_iot.central.services.device_template") @mock.patch("azext_iot.central.services.device") def test_validate_properties_name_miss_under_component( self, mock_device_svc, mock_device_template_svc ): # setup mock_device_template_svc.get_device_template.return_value = Template( self._duplicate_property_template ) monitor = PropertyMonitor( cmd=None, app_id=app_id, device_id=device_id, token=None, central_dns_suffix=None, ) # invalid component property definition = { PNP_DTDLV2_COMPONENT_MARKER: "c", "data": {"definition": "test_definition"}, } issues = monitor._validate_payload_against_entities( definition, list(definition.keys())[0], Severity.warning, ) assert ( issues[0].details == "Device is sending data that has not been defined in the device template. " "Following capabilities have NOT been defined in the device template '['data']'. " "Following capabilities have been defined in the device template (grouped by components) " "'{'_rpgcmdpo': ['component1Prop', 'testComponent', 'component1PropReadonly', 'component1Prop2'], " "'RS40OccupancySensorV36fy': ['component2prop', 'testComponent', 'component2PropReadonly', " "'component2Prop2', 'component1Telemetry']}'. " )
def _get_template(self): return central_models.TemplateV1( load_json(FileNames.central_device_template_file))
def _get_template(self): return Template(load_json(FileNames.central_device_template_file))
def test_object_simple(self, value, expected_result): template = central_models.TemplateV1( load_json(FileNames.central_device_template_file) ) schema = template.get_schema("Object") assert validate(schema, value) == expected_result