async def test_catch_request_error(hass: HomeAssistant, dms_device_mock: Mock) -> None: """Test errors when making requests to the device are handled.""" dms_device_mock.async_browse_metadata.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.NO_SUCH_OBJECT ) with pytest.raises(Unresolvable, match="No such object: bad_id"): await async_resolve_media(hass, ":bad_id") dms_device_mock.async_search_directory.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.INVALID_SEARCH_CRITERIA ) with pytest.raises(Unresolvable, match="Invalid query: bad query"): await async_resolve_media(hass, "?bad query") dms_device_mock.async_browse_metadata.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.CANNOT_PROCESS_REQUEST ) with pytest.raises(BrowseError, match="Server failure: "): await async_resolve_media(hass, ":good_id") dms_device_mock.async_browse_metadata.side_effect = UpnpError with pytest.raises( BrowseError, match="Server communication failure: UpnpError(.*)" ): await async_resolve_media(hass, ":bad_id")
async def test_catch_request_error(device_source_mock: DmsDeviceSource, dms_device_mock: Mock) -> None: """Test errors when making requests to the device are handled.""" dms_device_mock.async_browse_metadata.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.NO_SUCH_OBJECT) with pytest.raises(ActionError, match="No such object: bad_id"): await device_source_mock.async_resolve_media(":bad_id") dms_device_mock.async_search_directory.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.INVALID_SEARCH_CRITERIA) with pytest.raises(ActionError, match="Invalid query: bad query"): await device_source_mock.async_resolve_media("?bad query") dms_device_mock.async_browse_metadata.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.CANNOT_PROCESS_REQUEST) with pytest.raises(DeviceConnectionError, match="Server failure: "): await device_source_mock.async_resolve_media(":good_id") dms_device_mock.async_browse_metadata.side_effect = UpnpError with pytest.raises(DeviceConnectionError, match="Server communication failure: UpnpError(.*)"): await device_source_mock.async_resolve_media(":bad_id") # UpnpConnectionErrors will cause the device_source_mock to disconnect from the device assert device_source_mock.available dms_device_mock.async_browse_metadata.side_effect = UpnpConnectionError with pytest.raises(DeviceConnectionError, match="Server disconnected: UpnpConnectionError(.*)"): await device_source_mock.async_resolve_media(":bad_id") assert not device_source_mock.available
async def test_resolve_path_browsed_nothing( device_source_mock: DmsDeviceSource, dms_device_mock: Mock ) -> None: """Test async_resolve_path: action error results in browsing, but nothing found.""" dms_device_mock.async_search_directory.side_effect = UpnpActionError() # No children dms_device_mock.async_browse_direct_children.side_effect = [ DmsDevice.BrowseResult([], 0, 0, 0) ] with pytest.raises(Unresolvable, match="No contents for thing in thing/other"): await device_source_mock.async_resolve_path(r"thing/other") # There are children, but they don't match dms_device_mock.async_browse_direct_children.side_effect = [ DmsDevice.BrowseResult( [ didl_lite.Item( id="nothingid", restricted="false", title="not thing", res=[] ) ], 1, 1, 0, ) ] with pytest.raises(Unresolvable, match="Nothing found for thing in thing/other"): await device_source_mock.async_resolve_path(r"thing/other")
async def test_resolve_path_no_such_container(hass: HomeAssistant, dms_device_mock: Mock) -> None: """Test async_resolve_path: Explicit check for NO_SUCH_CONTAINER.""" dms_device_mock.async_search_directory.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.NO_SUCH_CONTAINER) with pytest.raises(Unresolvable, match="No such container: 0"): await async_resolve_media(hass, "thing/other")
async def test_browse_search_invalid(hass: HomeAssistant, dms_device_mock: Mock) -> None: """Test searching with an invalid query gives a BrowseError.""" query = "title == FooBar" dms_device_mock.async_search_directory.side_effect = UpnpActionError( error_code=ContentDirectoryErrorCode.INVALID_SEARCH_CRITERIA) with pytest.raises(BrowseError, match=f"Invalid query: {query}"): await async_browse_media(hass, f"?{query}")
async def test_resolve_path_browsed(hass: HomeAssistant, dms_device_mock: Mock) -> None: """Test async_resolve_path: action error results in browsing.""" path: Final = "path/to/thing" object_ids: Final = ["path_id", "to_id", "thing_id"] res_url: Final = "foo/bar" res_mime: Final = "audio/mpeg" # Setup expected calls search_directory_result = [] for ob_id, ob_title in zip(object_ids, path.split("/")): didl_item = didl_lite.Item( id=ob_id, restricted="false", title=ob_title, res=[], ) search_directory_result.append(DmsDevice.BrowseResult([didl_item], 1, 1, 0)) dms_device_mock.async_search_directory.side_effect = [ search_directory_result[0], # 2nd level can't be searched (this happens with Kodi) UpnpActionError(), search_directory_result[2], ] browse_children_result: BrowseResultList = [] for title in ("Irrelevant", "to", "Ignored"): browse_children_result.append( didl_lite.Item(id=f"{title}_id", restricted="false", title=title, res=[]) ) dms_device_mock.async_browse_direct_children.side_effect = [ DmsDevice.BrowseResult(browse_children_result, 3, 3, 0) ] dms_device_mock.async_browse_metadata.return_value = didl_lite.Item( id=object_ids[-1], restricted="false", title="thing", res=[didl_lite.Resource(uri=res_url, protocol_info=f"http-get:*:{res_mime}:")], ) # Perform the action to test result = await async_resolve_media(hass, path) # All levels should have an attempted search assert dms_device_mock.async_search_directory.await_args_list == [ call( parent_id, search_criteria=f'@parentID="{parent_id}" and dc:title="{title}"', metadata_filter=["id", "upnp:class", "dc:title"], requested_count=1, ) for parent_id, title in zip(["0"] + object_ids[:-1], path.split("/")) ] assert result.didl_metadata.id == object_ids[-1] # 2nd level should also be browsed assert dms_device_mock.async_browse_direct_children.await_args_list == [ call("path_id", metadata_filter=["id", "upnp:class", "dc:title"]) ]
async def test_resolve_path_browsed(device_source_mock: DmsDeviceSource, dms_device_mock: Mock) -> None: """Test async_resolve_path: action error results in browsing.""" path: Final = "path/to/thing" object_ids: Final = ["path_id", "to_id", "thing_id"] search_directory_result = [] for ob_id, ob_title in zip(object_ids, path.split("/")): didl_item = didl_lite.Item( id=ob_id, restricted="false", title=ob_title, res=[], ) search_directory_result.append( DmsDevice.BrowseResult([didl_item], 1, 1, 0)) dms_device_mock.async_search_directory.side_effect = [ search_directory_result[0], # 2nd level can't be searched (this happens with Kodi) UpnpActionError(), search_directory_result[2], ] browse_children_result: BrowseResultList = [] for title in ("Irrelevant", "to", "Ignored"): browse_children_result.append( didl_lite.Item(id=f"{title}_id", restricted="false", title=title, res=[])) dms_device_mock.async_browse_direct_children.side_effect = [ DmsDevice.BrowseResult(browse_children_result, 3, 3, 0) ] result = await device_source_mock.async_resolve_path(path) # All levels should have an attempted search assert dms_device_mock.async_search_directory.await_args_list == [ call( parent_id, search_criteria=f'@parentID="{parent_id}" and dc:title="{title}"', metadata_filter=["id", "upnp:class", "dc:title"], requested_count=1, ) for parent_id, title in zip(["0"] + object_ids[:-1], path.split("/")) ] assert result == object_ids[-1] # 2nd level should also be browsed assert dms_device_mock.async_browse_direct_children.await_args_list == [ call("path_id", metadata_filter=["id", "upnp:class", "dc:title"]) ]