mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Increase test coverage in Xbox integration (#162876)
This commit is contained in:
@@ -168,36 +168,40 @@ class XboxConsoleStatusCoordinator(XboxBaseCoordinator[dict[str, ConsoleData]]):
|
|||||||
_LOGGER.debug("%s status: %s", console.name, status.model_dump())
|
_LOGGER.debug("%s status: %s", console.name, status.model_dump())
|
||||||
|
|
||||||
# Setup focus app
|
# Setup focus app
|
||||||
app_details: Product | None = None
|
app_details = (
|
||||||
if (current_state := self.data.get(console.id)) is not None:
|
current_state.app_details
|
||||||
app_details = current_state.app_details
|
if (current_state := self.data.get(console.id)) is not None
|
||||||
|
and status.focus_app_aumid
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
if status.focus_app_aumid:
|
if status.focus_app_aumid and (
|
||||||
if (
|
not current_state
|
||||||
not current_state
|
or status.focus_app_aumid != current_state.status.focus_app_aumid
|
||||||
or status.focus_app_aumid != current_state.status.focus_app_aumid
|
):
|
||||||
):
|
catalog_result = (
|
||||||
app_id = status.focus_app_aumid.split("!")[0]
|
await self.client.catalog.get_product_from_alternate_id(
|
||||||
id_type = AlternateIdType.PACKAGE_FAMILY_NAME
|
*self._resolve_app_id(status.focus_app_aumid)
|
||||||
if app_id in SYSTEM_PFN_ID_MAP:
|
|
||||||
id_type = AlternateIdType.LEGACY_XBOX_PRODUCT_ID
|
|
||||||
app_id = SYSTEM_PFN_ID_MAP[app_id][id_type]
|
|
||||||
|
|
||||||
catalog_result = (
|
|
||||||
await self.client.catalog.get_product_from_alternate_id(
|
|
||||||
app_id, id_type
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if catalog_result.products:
|
if catalog_result.products:
|
||||||
app_details = catalog_result.products[0]
|
app_details = catalog_result.products[0]
|
||||||
else:
|
|
||||||
app_details = None
|
|
||||||
|
|
||||||
data[console.id] = ConsoleData(status=status, app_details=app_details)
|
data[console.id] = ConsoleData(status=status, app_details=app_details)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def _resolve_app_id(self, focus_app_aumid: str) -> tuple[str, AlternateIdType]:
|
||||||
|
app_id = focus_app_aumid.split("!", maxsplit=1)[0]
|
||||||
|
id_type = AlternateIdType.PACKAGE_FAMILY_NAME
|
||||||
|
|
||||||
|
if app_id in SYSTEM_PFN_ID_MAP:
|
||||||
|
id_type = AlternateIdType.LEGACY_XBOX_PRODUCT_ID
|
||||||
|
app_id = SYSTEM_PFN_ID_MAP[app_id][id_type]
|
||||||
|
|
||||||
|
return app_id, id_type
|
||||||
|
|
||||||
|
|
||||||
class XboxPresenceCoordinator(XboxBaseCoordinator[XboxData]):
|
class XboxPresenceCoordinator(XboxBaseCoordinator[XboxData]):
|
||||||
"""Update list of Xbox consoles."""
|
"""Update list of Xbox consoles."""
|
||||||
|
|||||||
@@ -148,10 +148,12 @@ class XboxMediaPlayer(XboxConsoleBaseEntity, MediaPlayerEntity):
|
|||||||
@property
|
@property
|
||||||
def media_content_type(self) -> MediaType:
|
def media_content_type(self) -> MediaType:
|
||||||
"""Media content type."""
|
"""Media content type."""
|
||||||
app_details = self.data.app_details
|
|
||||||
if app_details and app_details.product_family == "Games":
|
return (
|
||||||
return MediaType.GAME
|
MediaType.GAME
|
||||||
return MediaType.APP
|
if self.data.app_details and self.data.app_details.product_family == "Games"
|
||||||
|
else MediaType.APP
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_content_id(self) -> str | None:
|
def media_content_id(self) -> str | None:
|
||||||
@@ -161,11 +163,13 @@ class XboxMediaPlayer(XboxConsoleBaseEntity, MediaPlayerEntity):
|
|||||||
@property
|
@property
|
||||||
def media_title(self) -> str | None:
|
def media_title(self) -> str | None:
|
||||||
"""Title of current playing media."""
|
"""Title of current playing media."""
|
||||||
if not (app_details := self.data.app_details):
|
|
||||||
return None
|
|
||||||
return (
|
return (
|
||||||
app_details.localized_properties[0].product_title
|
(
|
||||||
or app_details.localized_properties[0].short_title
|
app_details.localized_properties[0].product_title
|
||||||
|
or app_details.localized_properties[0].short_title
|
||||||
|
)
|
||||||
|
if (app_details := self.data.app_details)
|
||||||
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"BigIds": ["9WZDNCRFJ3TJ"],
|
||||||
|
"HasMorePages": false,
|
||||||
|
"Products": [
|
||||||
|
{
|
||||||
|
"LocalizedProperties": [
|
||||||
|
{
|
||||||
|
"Images": [
|
||||||
|
{
|
||||||
|
"FileId": "2000000000037288315",
|
||||||
|
"EISListingIdentifier": null,
|
||||||
|
"BackgroundColor": "",
|
||||||
|
"Caption": "",
|
||||||
|
"FileSizeInBytes": 2514491,
|
||||||
|
"ForegroundColor": "",
|
||||||
|
"Height": 1080,
|
||||||
|
"ImagePositionInfo": "",
|
||||||
|
"ImagePurpose": "FeaturePromotionalSquareArt",
|
||||||
|
"UnscaledImageSHA256Hash": "i7GhC2HdYXf69+X/mLHtm8B36zR1gEOfuG2g2bXAAnY=",
|
||||||
|
"Uri": "//store-images.s-microsoft.com/image/apps.45451.65457035095819016.56f55216-1bb9-40aa-8796-068cf3075fc1.3abf2cc3-00cc-417d-a93d-97110cdfb261",
|
||||||
|
"Width": 1080
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ProductTitle": "Blue Dragon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MarketProperties": [],
|
||||||
|
"ProductBSchema": "ProductGame;1",
|
||||||
|
"ProductId": "C2HGK9J5367F",
|
||||||
|
"PartD": "",
|
||||||
|
"ProductFamily": "Games",
|
||||||
|
"ProductKind": "Game",
|
||||||
|
"DisplaySkuAvailabilities": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TotalResultCount": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"BigIds": ["9VWGNH0VBZJX"],
|
||||||
|
"HasMorePages": false,
|
||||||
|
"Products": [
|
||||||
|
{
|
||||||
|
"LastModifiedDate": "2014-10-21T17:30:01.0000000+00:00",
|
||||||
|
"LocalizedProperties": [
|
||||||
|
{
|
||||||
|
"Images": [
|
||||||
|
{
|
||||||
|
"FileSizeInBytes": 0,
|
||||||
|
"Height": 1080,
|
||||||
|
"ImagePositionInfo": "Xbox",
|
||||||
|
"ImagePurpose": "FeaturePromotionalSquareArt",
|
||||||
|
"SortOrder": "0",
|
||||||
|
"Uri": "https://images-eds-ssl.xboxlive.com/image?url=8Oaj9Ryq1G1_p3lLnXlsaZgGzAie6Mnu24_PawYuDYIoH77pJ.X5Z.MqQPibUVTcbx57bBxf63xu2Ef8acP3S7Uz80NbHc5nza..4R00GT1V5G760cdfX7Hl0uIHdHCbkzTikdvNE0TedhKgQfQy.2gjOGbd8kXZXzy4VzeJiNPLhLq2QUQbo8q3sVoSPaw73J4BxM7gaNX8V8qLcWtO5sn6vgbTso51OaEIn4zeAiw-",
|
||||||
|
"Width": 1080
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Language": "en-US",
|
||||||
|
"PublisherName": "Microsoft",
|
||||||
|
"SearchTitles": [
|
||||||
|
{ "SearchTitleString": "TV", "SearchTitleType": "SearchHint" }
|
||||||
|
],
|
||||||
|
"ShortTitle": "TV",
|
||||||
|
"SortTitle": "TV",
|
||||||
|
"Videos": [],
|
||||||
|
"VoiceTitle": "TV",
|
||||||
|
"ProductDescription": "",
|
||||||
|
"ProductTitle": "TV",
|
||||||
|
"Markets": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MarketProperties": [],
|
||||||
|
"ProductASchema": "Product;3",
|
||||||
|
"ProductBSchema": "ProductUnifiedApp;3",
|
||||||
|
"ProductId": "9VWGNH0VBZJX",
|
||||||
|
"Properties": {
|
||||||
|
"AllowedUrls": [],
|
||||||
|
"Categories": ["Video"],
|
||||||
|
"PublisherId": "",
|
||||||
|
"SkuDisplayGroups": [],
|
||||||
|
"RevisionId": "2014-10-21T17:30:01.0000000+00:00"
|
||||||
|
},
|
||||||
|
"AlternateIds": [
|
||||||
|
{
|
||||||
|
"IdType": "LegacyXboxProductId",
|
||||||
|
"Value": "71e7df12-89e0-4dc7-a5ff-a182fc2df94f"
|
||||||
|
},
|
||||||
|
{ "IdType": "XboxTitleId", "Value": "371594669" }
|
||||||
|
],
|
||||||
|
"DomainDataVersion": "",
|
||||||
|
"IngestionSource": "Bingbox App",
|
||||||
|
"IsMicrosoftProduct": false,
|
||||||
|
"ProductType": "Application",
|
||||||
|
"ValidationData": null,
|
||||||
|
"MerchandizingTags": null,
|
||||||
|
"SandboxId": "RETAIL",
|
||||||
|
"ProductFamily": "Apps",
|
||||||
|
"SchemaVersion": "1",
|
||||||
|
"IsSandboxedProduct": true,
|
||||||
|
"ProductKind": "Application",
|
||||||
|
"ProductPolicies": {},
|
||||||
|
"DisplaySkuAvailabilities": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TotalResultCount": 2
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"xuid": "2533274913657542",
|
"xuid": "2533274913657542",
|
||||||
"isFavorite": true,
|
"isFavorite": false,
|
||||||
"isFollowingCaller": true,
|
"isFollowingCaller": true,
|
||||||
"isFollowedByCaller": true,
|
"isFollowedByCaller": true,
|
||||||
"isIdentityShared": false,
|
"isIdentityShared": false,
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"status": {
|
||||||
|
"errorCode": "OK",
|
||||||
|
"errorMessage": null
|
||||||
|
},
|
||||||
|
"powerState": "On",
|
||||||
|
"playbackState": "Stopped",
|
||||||
|
"loginState": null,
|
||||||
|
"focusAppAumid": "",
|
||||||
|
"isTvConfigured": true,
|
||||||
|
"digitalAssistantRemoteControlEnabled": true,
|
||||||
|
"consoleStreamingEnabled": false,
|
||||||
|
"remoteManagementEnabled": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"status": {
|
||||||
|
"errorCode": "OK",
|
||||||
|
"errorMessage": null
|
||||||
|
},
|
||||||
|
"powerState": "On",
|
||||||
|
"playbackState": "Stopped",
|
||||||
|
"loginState": null,
|
||||||
|
"focusAppAumid": "Microsoft.Xbox.LiveTV_8wekyb3d8bbwe!Microsoft.Xbox.LiveTV.Application",
|
||||||
|
"isTvConfigured": true,
|
||||||
|
"digitalAssistantRemoteControlEnabled": true,
|
||||||
|
"consoleStreamingEnabled": false,
|
||||||
|
"remoteManagementEnabled": true
|
||||||
|
}
|
||||||
@@ -9588,7 +9588,7 @@
|
|||||||
'gamertag': '**REDACTED**',
|
'gamertag': '**REDACTED**',
|
||||||
'is_broadcasting': False,
|
'is_broadcasting': False,
|
||||||
'is_cloaked': None,
|
'is_cloaked': None,
|
||||||
'is_favorite': True,
|
'is_favorite': False,
|
||||||
'is_followed_by_caller': True,
|
'is_followed_by_caller': True,
|
||||||
'is_following_caller': True,
|
'is_following_caller': True,
|
||||||
'is_friend': True,
|
'is_friend': True,
|
||||||
|
|||||||
@@ -124,7 +124,7 @@
|
|||||||
'title': 'Installed Applications',
|
'title': 'Installed Applications',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_media_players[media_player.xone-entry]
|
# name: test_media_players[app][media_player.xone-entry]
|
||||||
EntityRegistryEntrySnapshot({
|
EntityRegistryEntrySnapshot({
|
||||||
'aliases': set({
|
'aliases': set({
|
||||||
}),
|
}),
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_media_players[media_player.xone-state]
|
# name: test_media_players[app][media_player.xone-state]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'entity_picture': 'https://store-images.s-microsoft.com/image/apps.9815.9007199266246365.7dc5d343-fe4a-40c3-93dd-c78e77f97331.45eebdef-f725-4799-bbf8-9ad8391a8279',
|
'entity_picture': 'https://store-images.s-microsoft.com/image/apps.9815.9007199266246365.7dc5d343-fe4a-40c3-93dd-c78e77f97331.45eebdef-f725-4799-bbf8-9ad8391a8279',
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
'state': 'on',
|
'state': 'on',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_media_players[media_player.xonex-entry]
|
# name: test_media_players[app][media_player.xonex-entry]
|
||||||
EntityRegistryEntrySnapshot({
|
EntityRegistryEntrySnapshot({
|
||||||
'aliases': set({
|
'aliases': set({
|
||||||
}),
|
}),
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_media_players[media_player.xonex-state]
|
# name: test_media_players[app][media_player.xonex-state]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'entity_picture': 'https://store-images.s-microsoft.com/image/apps.9815.9007199266246365.7dc5d343-fe4a-40c3-93dd-c78e77f97331.45eebdef-f725-4799-bbf8-9ad8391a8279',
|
'entity_picture': 'https://store-images.s-microsoft.com/image/apps.9815.9007199266246365.7dc5d343-fe4a-40c3-93dd-c78e77f97331.45eebdef-f725-4799-bbf8-9ad8391a8279',
|
||||||
@@ -236,3 +236,221 @@
|
|||||||
'state': 'on',
|
'state': 'on',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_media_players[idle][media_player.xone-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'media_player',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'media_player.xone',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'object_id_base': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': None,
|
||||||
|
'platform': 'xbox',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
'translation_key': 'xbox',
|
||||||
|
'unique_id': 'HIJKLMN',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[idle][media_player.xone-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'entity_picture_local': None,
|
||||||
|
'friendly_name': 'XONE',
|
||||||
|
'media_content_type': <MediaType.APP: 'app'>,
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'media_player.xone',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[idle][media_player.xonex-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'media_player',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'media_player.xonex',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'object_id_base': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': None,
|
||||||
|
'platform': 'xbox',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
'translation_key': 'xbox',
|
||||||
|
'unique_id': 'ABCDEFG',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[idle][media_player.xonex-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'entity_picture_local': None,
|
||||||
|
'friendly_name': 'XONEX',
|
||||||
|
'media_content_type': <MediaType.APP: 'app'>,
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'media_player.xonex',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[livetvapp][media_player.xone-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'media_player',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'media_player.xone',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'object_id_base': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': None,
|
||||||
|
'platform': 'xbox',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
'translation_key': 'xbox',
|
||||||
|
'unique_id': 'HIJKLMN',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[livetvapp][media_player.xone-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'entity_picture': 'https://images-eds-ssl.xboxlive.com/image?url=8Oaj9Ryq1G1_p3lLnXlsaZgGzAie6Mnu24_PawYuDYIoH77pJ.X5Z.MqQPibUVTcbx57bBxf63xu2Ef8acP3S7Uz80NbHc5nza..4R00GT1V5G760cdfX7Hl0uIHdHCbkzTikdvNE0TedhKgQfQy.2gjOGbd8kXZXzy4VzeJiNPLhLq2QUQbo8q3sVoSPaw73J4BxM7gaNX8V8qLcWtO5sn6vgbTso51OaEIn4zeAiw-',
|
||||||
|
'entity_picture_local': '/api/media_player_proxy/media_player.xone?token=mock_token&cache=cf419ddd9fb966d6',
|
||||||
|
'friendly_name': 'XONE',
|
||||||
|
'media_content_id': '9VWGNH0VBZJX',
|
||||||
|
'media_content_type': <MediaType.APP: 'app'>,
|
||||||
|
'media_title': 'TV',
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'media_player.xone',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[livetvapp][media_player.xonex-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'media_player',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'media_player.xonex',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'object_id_base': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': None,
|
||||||
|
'platform': 'xbox',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
'translation_key': 'xbox',
|
||||||
|
'unique_id': 'ABCDEFG',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_media_players[livetvapp][media_player.xonex-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'entity_picture': 'https://images-eds-ssl.xboxlive.com/image?url=8Oaj9Ryq1G1_p3lLnXlsaZgGzAie6Mnu24_PawYuDYIoH77pJ.X5Z.MqQPibUVTcbx57bBxf63xu2Ef8acP3S7Uz80NbHc5nza..4R00GT1V5G760cdfX7Hl0uIHdHCbkzTikdvNE0TedhKgQfQy.2gjOGbd8kXZXzy4VzeJiNPLhLq2QUQbo8q3sVoSPaw73J4BxM7gaNX8V8qLcWtO5sn6vgbTso51OaEIn4zeAiw-',
|
||||||
|
'entity_picture_local': '/api/media_player_proxy/media_player.xonex?token=mock_token&cache=cf419ddd9fb966d6',
|
||||||
|
'friendly_name': 'XONEX',
|
||||||
|
'media_content_id': '9VWGNH0VBZJX',
|
||||||
|
'media_content_type': <MediaType.APP: 'app'>,
|
||||||
|
'media_title': 'TV',
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 149385>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'media_player.xonex',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
|
from httpx import HTTPStatusError, RequestError, TimeoutException
|
||||||
import pytest
|
import pytest
|
||||||
from pythonxbox.api.provider.people.models import PeopleResponse
|
from pythonxbox.api.provider.people.models import PeopleResponse
|
||||||
|
|
||||||
@@ -22,7 +23,10 @@ from homeassistant.config_entries import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
from homeassistant.helpers import config_entry_oauth2_flow
|
from homeassistant.helpers import config_entry_oauth2_flow, device_registry as dr
|
||||||
|
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||||
|
ImplementationUnavailableError,
|
||||||
|
)
|
||||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||||
from homeassistant.helpers.service_info.ssdp import SsdpServiceInfo
|
from homeassistant.helpers.service_info.ssdp import SsdpServiceInfo
|
||||||
|
|
||||||
@@ -576,7 +580,10 @@ async def test_add_friend_flow_config_entry_not_loaded(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("xbox_live_client", "authentication_manager")
|
@pytest.mark.usefixtures("xbox_live_client", "authentication_manager")
|
||||||
async def test_unique_id_and_friends_migration(hass: HomeAssistant) -> None:
|
async def test_unique_id_and_friends_migration(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
) -> None:
|
||||||
"""Test config entry unique_id migration and favorite to subentry migration."""
|
"""Test config entry unique_id migration and favorite to subentry migration."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
@@ -601,6 +608,17 @@ async def test_unique_id_and_friends_migration(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
device_own = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=config_entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, "xbox_live")},
|
||||||
|
)
|
||||||
|
|
||||||
|
device_friend = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=config_entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, "2533274838782903")},
|
||||||
|
)
|
||||||
|
assert device_friend.config_entries_subentries[config_entry.entry_id] == {None}
|
||||||
|
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@@ -611,14 +629,111 @@ async def test_unique_id_and_friends_migration(hass: HomeAssistant) -> None:
|
|||||||
assert config_entry.title == "GSR Ae"
|
assert config_entry.title == "GSR Ae"
|
||||||
|
|
||||||
# Assert favorite friends migrated to subentries
|
# Assert favorite friends migrated to subentries
|
||||||
assert len(config_entry.subentries) == 2
|
assert len(config_entry.subentries) == 1
|
||||||
subentries = list(config_entry.subentries.values())
|
subentries = list(config_entry.subentries.values())
|
||||||
assert subentries[0].unique_id == "2533274838782903"
|
assert subentries[0].unique_id == "2533274838782903"
|
||||||
assert subentries[0].title == "Ikken Hissatsuu"
|
assert subentries[0].title == "Ikken Hissatsuu"
|
||||||
assert subentries[0].subentry_type == "friend"
|
assert subentries[0].subentry_type == "friend"
|
||||||
assert subentries[1].unique_id == "2533274913657542"
|
|
||||||
assert subentries[1].title == "erics273"
|
## Assert devices have been migrated
|
||||||
assert subentries[1].subentry_type == "friend"
|
assert (device_own := device_registry.async_get(device_own.id))
|
||||||
|
assert device_own.identifiers == {(DOMAIN, "271958441785640")}
|
||||||
|
|
||||||
|
assert (device_friend := device_registry.async_get(device_friend.id))
|
||||||
|
assert device_friend.config_entries_subentries[config_entry.entry_id] == {
|
||||||
|
subentries[0].subentry_id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("provider", "method"),
|
||||||
|
[
|
||||||
|
("people", "get_friends_by_xuid"),
|
||||||
|
("people", "get_friends_own"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception",
|
||||||
|
[
|
||||||
|
TimeoutException(""),
|
||||||
|
RequestError("", request=Mock()),
|
||||||
|
HTTPStatusError("", request=Mock(), response=Mock()),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("authentication_manager")
|
||||||
|
async def test_migration_exceptions(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
xbox_live_client: AsyncMock,
|
||||||
|
provider: str,
|
||||||
|
method: str,
|
||||||
|
exception: Exception,
|
||||||
|
) -> None:
|
||||||
|
"""Test exceptions during migration."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Home Assistant Cloud",
|
||||||
|
data={
|
||||||
|
"auth_implementation": "cloud",
|
||||||
|
"token": {
|
||||||
|
"access_token": "1234567890",
|
||||||
|
"expires_at": 1760697327.7298331,
|
||||||
|
"expires_in": 3600,
|
||||||
|
"refresh_token": "0987654321",
|
||||||
|
"scope": "XboxLive.signin XboxLive.offline_access",
|
||||||
|
"service": "xbox",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unique_id=DOMAIN,
|
||||||
|
version=1,
|
||||||
|
minor_version=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
provider = getattr(xbox_live_client, provider)
|
||||||
|
getattr(provider, method).side_effect = exception
|
||||||
|
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert config_entry.state is config_entries.ConfigEntryState.MIGRATION_ERROR
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("xbox_live_client", "authentication_manager")
|
||||||
|
async def test_migration_implementation_unavailable(hass: HomeAssistant) -> None:
|
||||||
|
"""Test implementation unavailable exception during migration."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Home Assistant Cloud",
|
||||||
|
data={
|
||||||
|
"auth_implementation": "cloud",
|
||||||
|
"token": {
|
||||||
|
"access_token": "1234567890",
|
||||||
|
"expires_at": 1760697327.7298331,
|
||||||
|
"expires_in": 3600,
|
||||||
|
"refresh_token": "0987654321",
|
||||||
|
"scope": "XboxLive.signin XboxLive.offline_access",
|
||||||
|
"service": "xbox",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unique_id=DOMAIN,
|
||||||
|
version=1,
|
||||||
|
minor_version=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.xbox.async_get_config_entry_implementation",
|
||||||
|
side_effect=ImplementationUnavailableError,
|
||||||
|
):
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert config_entry.state is config_entries.ConfigEntryState.MIGRATION_ERROR
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(
|
@pytest.mark.usefixtures(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Tests for the Xbox integration."""
|
"""Tests for the Xbox integration."""
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from httpx import ConnectTimeout, HTTPStatusError, ProtocolError
|
from httpx import ConnectTimeout, HTTPStatusError, ProtocolError
|
||||||
@@ -42,7 +42,11 @@ async def test_entry_setup_unload(
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exception",
|
"exception",
|
||||||
[ConnectTimeout, HTTPStatusError, ProtocolError],
|
[
|
||||||
|
ConnectTimeout(""),
|
||||||
|
HTTPStatusError("", request=Mock(), response=Mock()),
|
||||||
|
ProtocolError(""),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
async def test_config_entry_not_ready(
|
async def test_config_entry_not_ready(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@@ -78,7 +82,14 @@ async def test_config_implementation_not_available(
|
|||||||
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("exception", [ConnectTimeout, HTTPStatusError, ProtocolError])
|
@pytest.mark.parametrize(
|
||||||
|
"exception",
|
||||||
|
[
|
||||||
|
ConnectTimeout(""),
|
||||||
|
HTTPStatusError("", request=Mock(), response=Mock()),
|
||||||
|
ProtocolError(""),
|
||||||
|
],
|
||||||
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("provider", "method"),
|
("provider", "method"),
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
from httpx import HTTPStatusError, RequestError, TimeoutException
|
from httpx import HTTPStatusError, RequestError, TimeoutException
|
||||||
import pytest
|
import pytest
|
||||||
|
from pythonxbox.api.provider.catalog.models import CatalogResponse
|
||||||
from pythonxbox.api.provider.smartglass.models import (
|
from pythonxbox.api.provider.smartglass.models import (
|
||||||
SmartglassConsoleStatus,
|
SmartglassConsoleStatus,
|
||||||
VolumeDirection,
|
VolumeDirection,
|
||||||
@@ -67,15 +68,37 @@ def mock_token() -> Generator[MagicMock]:
|
|||||||
yield token
|
yield token
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("xbox_live_client")
|
@pytest.mark.parametrize(
|
||||||
|
("fixture_status", "fixture_catalog"),
|
||||||
|
[
|
||||||
|
("smartglass_console_status.json", "catalog_product_lookup.json"),
|
||||||
|
("smartglass_console_status_idle.json", "catalog_product_lookup.json"),
|
||||||
|
("smartglass_console_status_livetv.json", "catalog_product_lookup_livetv.json"),
|
||||||
|
],
|
||||||
|
ids=["app", "idle", "livetvapp"],
|
||||||
|
)
|
||||||
async def test_media_players(
|
async def test_media_players(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: MockConfigEntry,
|
config_entry: MockConfigEntry,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
|
xbox_live_client: AsyncMock,
|
||||||
|
fixture_status: str,
|
||||||
|
fixture_catalog: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test setup of the Xbox media player platform."""
|
"""Test setup of the Xbox media player platform."""
|
||||||
|
|
||||||
|
xbox_live_client.smartglass.get_console_status.return_value = (
|
||||||
|
SmartglassConsoleStatus(
|
||||||
|
**await async_load_json_object_fixture(hass, fixture_status, DOMAIN) # pyright: ignore[reportArgumentType]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
xbox_live_client.catalog.get_product_from_alternate_id.return_value = (
|
||||||
|
CatalogResponse(
|
||||||
|
**await async_load_json_object_fixture(hass, fixture_catalog, DOMAIN) # pyright: ignore[reportArgumentType]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@@ -237,11 +260,11 @@ async def test_media_player_actions(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exception",
|
("exception", "translation_key"),
|
||||||
[
|
[
|
||||||
TimeoutException(""),
|
(TimeoutException(""), "timeout_exception"),
|
||||||
RequestError("", request=Mock()),
|
(RequestError("", request=Mock()), "request_exception"),
|
||||||
HTTPStatusError("", request=Mock(), response=Mock()),
|
(HTTPStatusError("", request=Mock(), response=Mock()), "request_exception"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_media_player_action_exceptions(
|
async def test_media_player_action_exceptions(
|
||||||
@@ -252,6 +275,7 @@ async def test_media_player_action_exceptions(
|
|||||||
service_args: dict[str, Any],
|
service_args: dict[str, Any],
|
||||||
call_method: str,
|
call_method: str,
|
||||||
exception: Exception,
|
exception: Exception,
|
||||||
|
translation_key: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test media player action exceptions."""
|
"""Test media player action exceptions."""
|
||||||
|
|
||||||
@@ -271,13 +295,14 @@ async def test_media_player_action_exceptions(
|
|||||||
|
|
||||||
getattr(xbox_live_client.smartglass, call_method).side_effect = exception
|
getattr(xbox_live_client.smartglass, call_method).side_effect = exception
|
||||||
|
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError) as e:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
MEDIA_PLAYER_DOMAIN,
|
MEDIA_PLAYER_DOMAIN,
|
||||||
service,
|
service,
|
||||||
target={ATTR_ENTITY_ID: "media_player.xone", **service_args},
|
target={ATTR_ENTITY_ID: "media_player.xone", **service_args},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == translation_key
|
||||||
|
|
||||||
|
|
||||||
async def test_media_player_turn_on_failed(
|
async def test_media_player_turn_on_failed(
|
||||||
@@ -299,10 +324,11 @@ async def test_media_player_turn_on_failed(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError) as e:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
MEDIA_PLAYER_DOMAIN,
|
MEDIA_PLAYER_DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
target={ATTR_ENTITY_ID: "media_player.xone"},
|
target={ATTR_ENTITY_ID: "media_player.xone"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == "turn_on_failed"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Tests for the Xbox media source platform."""
|
"""Tests for the Xbox media source platform."""
|
||||||
|
|
||||||
import httpx
|
from httpx import HTTPStatusError, RequestError, Response, TimeoutException
|
||||||
import pytest
|
import pytest
|
||||||
from pythonxbox.api.provider.people.models import PeopleResponse
|
from pythonxbox.api.provider.people.models import PeopleResponse
|
||||||
from syrupy.assertion import SnapshotAssertion
|
from syrupy.assertion import SnapshotAssertion
|
||||||
@@ -173,14 +173,30 @@ async def test_browse_media_accounts(
|
|||||||
"titlehub",
|
"titlehub",
|
||||||
"get_title_info",
|
"get_title_info",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"/271958441785640/1297287135/community_gameclips",
|
||||||
|
"gameclips",
|
||||||
|
"get_recent_community_clips_by_title_id",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/271958441785640/1297287135/community_screenshots",
|
||||||
|
"screenshots",
|
||||||
|
"get_recent_community_screenshots_by_title_id",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exception",
|
("exception", "translation_key"),
|
||||||
[
|
[
|
||||||
httpx.HTTPStatusError("", request=MagicMock(), response=httpx.Response(500)),
|
(
|
||||||
httpx.RequestError(""),
|
HTTPStatusError("", request=MagicMock(), response=Response(500)),
|
||||||
httpx.TimeoutException(""),
|
"request_exception",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
RequestError(""),
|
||||||
|
"request_exception",
|
||||||
|
),
|
||||||
|
(TimeoutException(""), "timeout_exception"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_browse_media_exceptions(
|
async def test_browse_media_exceptions(
|
||||||
@@ -191,6 +207,7 @@ async def test_browse_media_exceptions(
|
|||||||
provider: str,
|
provider: str,
|
||||||
method: str,
|
method: str,
|
||||||
exception: Exception,
|
exception: Exception,
|
||||||
|
translation_key: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test browsing media exceptions."""
|
"""Test browsing media exceptions."""
|
||||||
|
|
||||||
@@ -203,8 +220,9 @@ async def test_browse_media_exceptions(
|
|||||||
provider = getattr(xbox_live_client, provider)
|
provider = getattr(xbox_live_client, provider)
|
||||||
getattr(provider, method).side_effect = exception
|
getattr(provider, method).side_effect = exception
|
||||||
|
|
||||||
with pytest.raises(BrowseError):
|
with pytest.raises(BrowseError) as e:
|
||||||
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}{media_content_id}")
|
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}{media_content_id}")
|
||||||
|
assert e.value.translation_key == translation_key
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("xbox_live_client")
|
@pytest.mark.usefixtures("xbox_live_client")
|
||||||
@@ -239,8 +257,9 @@ async def test_browse_media_not_configured_exception(
|
|||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
with pytest.raises(BrowseError, match="The Xbox integration is not configured"):
|
with pytest.raises(BrowseError) as e:
|
||||||
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}")
|
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}")
|
||||||
|
assert e.value.translation_key == "xbox_not_configured"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("xbox_live_client")
|
@pytest.mark.usefixtures("xbox_live_client")
|
||||||
@@ -256,8 +275,9 @@ async def test_browse_media_account_not_configured_exception(
|
|||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
with pytest.raises(BrowseError):
|
with pytest.raises(BrowseError) as e:
|
||||||
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/2533274838782903")
|
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/2533274838782903")
|
||||||
|
assert e.value.translation_key == "account_not_configured"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -278,8 +298,24 @@ async def test_browse_media_account_not_configured_exception(
|
|||||||
"https://store-images.s-microsoft.com/image/apps.35725.65457035095819016.56f55216-1bb9-40aa-8796-068cf3075fc1.c4bf34f8-ad40-4af3-914e-a85e75a76bed",
|
"https://store-images.s-microsoft.com/image/apps.35725.65457035095819016.56f55216-1bb9-40aa-8796-068cf3075fc1.c4bf34f8-ad40-4af3-914e-a85e75a76bed",
|
||||||
"image/png",
|
"image/png",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"/271958441785640/1297287135/community_screenshots/504a78e5-be24-4020-a245-77cb528e91ea",
|
||||||
|
"https://screenshotscontent-d5002.media.xboxlive.com/xuid-2535422966774043-private/504a78e5-be24-4020-a245-77cb528e91ea.PNG?skoid=296fcea0-0bf0-4a22-abf7-16b3524eba1b&sktid=68cd85cc-e0b3-43c8-ba3c-67686dbf8a67&skt=2025-11-06T10%3A21%3A06Z&ske=2025-11-07T10%3A21%3A06Z&sks=b&skv=2025-05-05&sv=2025-05-05&st=2025-11-06T10%3A35%3A59Z&se=2125-11-06T10%3A50%3A59Z&sr=b&sp=r&sig=TqUUNeuAzHawaXBTFfSVuUzuXbGOMgrDu0Q2VBTFd5U%3D",
|
||||||
|
"image/png",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/271958441785640/1297287135/community_gameclips/6fa2731a-8b58-4aa6-848c-4bf15734358b",
|
||||||
|
"https://gameclipscontent-d3021.media.xboxlive.com/xuid-2535458333395495-private/6fa2731a-8b58-4aa6-848c-4bf15734358b.MP4?skoid=2938738c-0e58-4f21-9b82-98081ade42e2&sktid=68cd85cc-e0b3-43c8-ba3c-67686dbf8a67&skt=2025-11-06T08%3A20%3A51Z&ske=2025-11-07T08%3A20%3A51Z&sks=b&skv=2025-05-05&sv=2025-05-05&st=2025-11-06T10%3A05%3A41Z&se=2125-11-06T10%3A20%3A41Z&sr=b&sp=r&sig=s%2FWDtmE2cnAwl9iJJFcch3knbRlkxkALoinHQwCnNP0%3D&__gda__=1762438393_eb8a56c3f482d00099045aa892a2aa05",
|
||||||
|
"video/mp4",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"screenshot",
|
||||||
|
"gameclips",
|
||||||
|
"game_media",
|
||||||
|
"community_screenshots",
|
||||||
|
"community_gameclips",
|
||||||
],
|
],
|
||||||
ids=["screenshot", "gameclips", "game_media"],
|
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("xbox_live_client")
|
@pytest.mark.usefixtures("xbox_live_client")
|
||||||
async def test_resolve_media(
|
async def test_resolve_media(
|
||||||
@@ -319,6 +355,16 @@ async def test_resolve_media(
|
|||||||
"gameclips",
|
"gameclips",
|
||||||
"get_recent_clips_by_xuid",
|
"get_recent_clips_by_xuid",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"/271958441785640/1297287135/community_screenshots/504a78e5-be24-4020-a245-77cb528e91ea",
|
||||||
|
"screenshots",
|
||||||
|
"get_recent_community_screenshots_by_title_id",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/271958441785640/1297287135/community_gameclips/6fa2731a-8b58-4aa6-848c-4bf15734358b",
|
||||||
|
"gameclips",
|
||||||
|
"get_recent_community_clips_by_title_id",
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"/271958441785640/1297287135/game_media/0",
|
"/271958441785640/1297287135/game_media/0",
|
||||||
"titlehub",
|
"titlehub",
|
||||||
@@ -327,11 +373,14 @@ async def test_resolve_media(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exception",
|
("exception", "translation_key"),
|
||||||
[
|
[
|
||||||
httpx.HTTPStatusError("", request=MagicMock(), response=httpx.Response(500)),
|
(
|
||||||
httpx.RequestError(""),
|
HTTPStatusError("", request=MagicMock(), response=Response(500)),
|
||||||
httpx.TimeoutException(""),
|
"request_exception",
|
||||||
|
),
|
||||||
|
(RequestError(""), "request_exception"),
|
||||||
|
(TimeoutException(""), "timeout_exception"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_resolve_media_exceptions(
|
async def test_resolve_media_exceptions(
|
||||||
@@ -342,6 +391,7 @@ async def test_resolve_media_exceptions(
|
|||||||
provider: str,
|
provider: str,
|
||||||
method: str,
|
method: str,
|
||||||
exception: Exception,
|
exception: Exception,
|
||||||
|
translation_key: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test resolve media exceptions."""
|
"""Test resolve media exceptions."""
|
||||||
|
|
||||||
@@ -354,12 +404,13 @@ async def test_resolve_media_exceptions(
|
|||||||
provider = getattr(xbox_live_client, provider)
|
provider = getattr(xbox_live_client, provider)
|
||||||
getattr(provider, method).side_effect = exception
|
getattr(provider, method).side_effect = exception
|
||||||
|
|
||||||
with pytest.raises(Unresolvable):
|
with pytest.raises(Unresolvable) as e:
|
||||||
await async_resolve_media(
|
await async_resolve_media(
|
||||||
hass,
|
hass,
|
||||||
f"{URI_SCHEME}{DOMAIN}{media_content_id}",
|
f"{URI_SCHEME}{DOMAIN}{media_content_id}",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == translation_key
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("media_type"), ["screenshots", "gameclips", "game_media"])
|
@pytest.mark.parametrize(("media_type"), ["screenshots", "gameclips", "game_media"])
|
||||||
@@ -377,12 +428,13 @@ async def test_resolve_media_not_found_exceptions(
|
|||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
with pytest.raises(Unresolvable, match="The requested media could not be found"):
|
with pytest.raises(Unresolvable) as e:
|
||||||
await async_resolve_media(
|
await async_resolve_media(
|
||||||
hass,
|
hass,
|
||||||
f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/{media_type}/12345",
|
f"{URI_SCHEME}{DOMAIN}/271958441785640/1297287135/{media_type}/12345",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == "media_not_found"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("xbox_live_client")
|
@pytest.mark.usefixtures("xbox_live_client")
|
||||||
@@ -416,12 +468,13 @@ async def test_resolve_media_not_configured(
|
|||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
with pytest.raises(Unresolvable, match="The Xbox integration is not configured"):
|
with pytest.raises(Unresolvable) as e:
|
||||||
await async_resolve_media(
|
await async_resolve_media(
|
||||||
hass,
|
hass,
|
||||||
f"{URI_SCHEME}{DOMAIN}/2533274838782903",
|
f"{URI_SCHEME}{DOMAIN}/2533274838782903",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == "xbox_not_configured"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("xbox_live_client")
|
@pytest.mark.usefixtures("xbox_live_client")
|
||||||
@@ -437,9 +490,10 @@ async def test_resolve_media_account_not_configured(
|
|||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
with pytest.raises(Unresolvable, match="The Xbox account is not configured"):
|
with pytest.raises(Unresolvable) as e:
|
||||||
await async_resolve_media(
|
await async_resolve_media(
|
||||||
hass,
|
hass,
|
||||||
f"{URI_SCHEME}{DOMAIN}/2533274838782903",
|
f"{URI_SCHEME}{DOMAIN}/2533274838782903",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == "account_not_configured"
|
||||||
|
|||||||
@@ -248,9 +248,7 @@ async def test_send_command_exceptions(
|
|||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
getattr(xbox_live_client.smartglass, call_method).side_effect = exception
|
getattr(xbox_live_client.smartglass, call_method).side_effect = exception
|
||||||
with pytest.raises(
|
with pytest.raises(HomeAssistantError) as e:
|
||||||
HomeAssistantError, check=lambda e: e.translation_key == translation_key
|
|
||||||
):
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
REMOTE_DOMAIN,
|
REMOTE_DOMAIN,
|
||||||
SERVICE_SEND_COMMAND,
|
SERVICE_SEND_COMMAND,
|
||||||
@@ -258,6 +256,7 @@ async def test_send_command_exceptions(
|
|||||||
target={ATTR_ENTITY_ID: "remote.xone"},
|
target={ATTR_ENTITY_ID: "remote.xone"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == translation_key
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -290,15 +289,14 @@ async def test_turn_on_exceptions(
|
|||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
xbox_live_client.smartglass.wake_up.side_effect = exception
|
xbox_live_client.smartglass.wake_up.side_effect = exception
|
||||||
with pytest.raises(
|
with pytest.raises(HomeAssistantError) as e:
|
||||||
HomeAssistantError, check=lambda e: e.translation_key == translation_key
|
|
||||||
):
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
REMOTE_DOMAIN,
|
REMOTE_DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
target={ATTR_ENTITY_ID: "remote.xone"},
|
target={ATTR_ENTITY_ID: "remote.xone"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == translation_key
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -325,12 +323,11 @@ async def test_turn_off_exceptions(
|
|||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
xbox_live_client.smartglass.turn_off.side_effect = exception
|
xbox_live_client.smartglass.turn_off.side_effect = exception
|
||||||
with pytest.raises(
|
with pytest.raises(HomeAssistantError) as e:
|
||||||
HomeAssistantError, check=lambda e: e.translation_key == translation_key
|
|
||||||
):
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
REMOTE_DOMAIN,
|
REMOTE_DOMAIN,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
target={ATTR_ENTITY_ID: "remote.xone"},
|
target={ATTR_ENTITY_ID: "remote.xone"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
assert e.value.translation_key == translation_key
|
||||||
|
|||||||
Reference in New Issue
Block a user