From b46b32bafa1f45ce988d3b5463a6ba71f99f428d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 19 Jan 2022 13:41:02 +0100 Subject: [PATCH] Add diagnostics to GitHub integration (#64385) --- .../components/github/diagnostics.py | 45 +++++++++++++ tests/components/github/conftest.py | 27 ++++++++ tests/components/github/test_config_flow.py | 4 +- tests/components/github/test_diagnostics.py | 63 +++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/github/diagnostics.py create mode 100644 tests/components/github/test_diagnostics.py diff --git a/homeassistant/components/github/diagnostics.py b/homeassistant/components/github/diagnostics.py new file mode 100644 index 00000000000..101bf642c91 --- /dev/null +++ b/homeassistant/components/github/diagnostics.py @@ -0,0 +1,45 @@ +"""Diagnostics support for the GitHub integration.""" +from __future__ import annotations + +from typing import Any + +from aiogithubapi import GitHubAPI, GitHubException + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import ( + SERVER_SOFTWARE, + async_get_clientsession, +) + +from .const import CONF_ACCESS_TOKEN, DOMAIN +from .coordinator import DataUpdateCoordinators + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, + config_entry: ConfigEntry, +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + data = {"options": {**config_entry.options}} + client = GitHubAPI( + token=config_entry.data[CONF_ACCESS_TOKEN], + session=async_get_clientsession(hass), + **{"client_name": SERVER_SOFTWARE}, + ) + + try: + rate_limit_response = await client.rate_limit() + except GitHubException as err: + data["rate_limit"] = {"error": str(err)} + else: + data["rate_limit"] = rate_limit_response.data.as_dict + + repositories: dict[str, DataUpdateCoordinators] = hass.data[DOMAIN] + data["repositories"] = {} + + for repository, coordinators in repositories.items(): + info = coordinators["information"].data + data["repositories"][repository] = info.as_dict if info else None + + return data diff --git a/tests/components/github/conftest.py b/tests/components/github/conftest.py index bcceea56bc9..bfaa8dda726 100644 --- a/tests/components/github/conftest.py +++ b/tests/components/github/conftest.py @@ -10,10 +10,12 @@ from homeassistant.components.github.const import ( DEFAULT_REPOSITORIES, DOMAIN, ) +from homeassistant.core import HomeAssistant from .common import MOCK_ACCESS_TOKEN from tests.common import MockConfigEntry +from tests.test_util.aiohttp import AiohttpClientMocker @pytest.fixture @@ -32,3 +34,28 @@ def mock_setup_entry() -> Generator[None, None, None]: """Mock setting up a config entry.""" with patch("homeassistant.components.github.async_setup_entry", return_value=True): yield + + +@pytest.fixture +async def setup_github_integration( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + aioclient_mock: AiohttpClientMocker, +) -> Generator[None, None, None]: + """Mock setting up the integration.""" + aioclient_mock.get( + "https://api.github.com/repos/home-assistant/core", + json={}, + headers={"Content-Type": "application/json"}, + ) + for endpoint in ("issues", "pulls", "releases", "commits"): + aioclient_mock.get( + f"https://api.github.com/repos/home-assistant/core/{endpoint}", + json=[], + headers={"Content-Type": "application/json"}, + ) + mock_config_entry.options = {CONF_REPOSITORIES: ["home-assistant/core"]} + mock_config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/github/test_config_flow.py b/tests/components/github/test_config_flow.py index f040cbd2998..dad97472620 100644 --- a/tests/components/github/test_config_flow.py +++ b/tests/components/github/test_config_flow.py @@ -90,7 +90,7 @@ async def test_flow_with_registration_failure( """Test flow with registration failure of the device.""" aioclient_mock.post( "https://github.com/login/device/code", - side_effect=GitHubException("Registration failed"), + exc=GitHubException("Registration failed"), ) result = await hass.config_entries.flow.async_init( DOMAIN, @@ -118,7 +118,7 @@ async def test_flow_with_activation_failure( ) aioclient_mock.post( "https://github.com/login/oauth/access_token", - side_effect=GitHubException("Activation failed"), + exc=GitHubException("Activation failed"), ) result = await hass.config_entries.flow.async_init( DOMAIN, diff --git a/tests/components/github/test_diagnostics.py b/tests/components/github/test_diagnostics.py new file mode 100644 index 00000000000..e4a1d257c2e --- /dev/null +++ b/tests/components/github/test_diagnostics.py @@ -0,0 +1,63 @@ +"""Test GitHub diagnostics.""" +from collections.abc import Generator + +from aiogithubapi import GitHubException +from aiohttp import ClientSession + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry +from tests.components.diagnostics import get_diagnostics_for_config_entry +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_entry_diagnostics( + hass: HomeAssistant, + hass_client: ClientSession, + mock_config_entry: MockConfigEntry, + aioclient_mock: AiohttpClientMocker, + setup_github_integration: Generator[None, None, None], +) -> None: + """Test config entry diagnostics.""" + aioclient_mock.get( + "https://api.github.com/rate_limit", + json={"resources": {"core": {"remaining": 100, "limit": 100}}}, + headers={"Content-Type": "application/json"}, + ) + + result = await get_diagnostics_for_config_entry( + hass, + hass_client, + mock_config_entry, + ) + + assert result["options"]["repositories"] == ["home-assistant/core"] + assert result["rate_limit"] == { + "resources": {"core": {"remaining": 100, "limit": 100}} + } + assert result["repositories"]["home-assistant/core"] == {} + + +async def test_entry_diagnostics_exception( + hass: HomeAssistant, + hass_client: ClientSession, + mock_config_entry: MockConfigEntry, + aioclient_mock: AiohttpClientMocker, + setup_github_integration: Generator[None, None, None], +) -> None: + """Test config entry diagnostics with exception for ratelimit.""" + aioclient_mock.get( + "https://api.github.com/rate_limit", + exc=GitHubException("error"), + ) + + result = await get_diagnostics_for_config_entry( + hass, + hass_client, + mock_config_entry, + ) + + assert ( + result["rate_limit"]["error"] + == "Unexpected exception for 'https://api.github.com/rate_limit' with - error" + )