mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Refactor GitHub tests to patch the library instead (#160568)
This commit is contained in:
committed by
GitHub
parent
b700a27c8f
commit
bbe1d28e88
@@ -1 +1,13 @@
|
||||
"""Tests for the GitHub integration."""
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
||||
"""Method for setting up the component."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
"""Common helpers for GitHub integration tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from homeassistant.components.github.const import CONF_REPOSITORIES, DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, async_load_fixture
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
MOCK_ACCESS_TOKEN = "gho_16C7e42F292c6912E7710c838347Ae178B4a"
|
||||
TEST_REPOSITORY = "octocat/Hello-World"
|
||||
|
||||
|
||||
async def setup_github_integration(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
add_entry_to_hass: bool = True,
|
||||
) -> None:
|
||||
"""Mock setting up the integration."""
|
||||
headers = json.loads(await async_load_fixture(hass, "base_headers.json", DOMAIN))
|
||||
for idx, repository in enumerate(mock_config_entry.options[CONF_REPOSITORIES]):
|
||||
aioclient_mock.get(
|
||||
f"https://api.github.com/repos/{repository}",
|
||||
json={
|
||||
**json.loads(await async_load_fixture(hass, "repository.json", DOMAIN)),
|
||||
"full_name": repository,
|
||||
"id": idx,
|
||||
},
|
||||
headers=headers,
|
||||
)
|
||||
aioclient_mock.get(
|
||||
f"https://api.github.com/repos/{repository}/events",
|
||||
json=[],
|
||||
headers=headers,
|
||||
)
|
||||
aioclient_mock.post(
|
||||
"https://api.github.com/graphql",
|
||||
json=json.loads(await async_load_fixture(hass, "graphql.json", DOMAIN)),
|
||||
headers=headers,
|
||||
)
|
||||
if add_entry_to_hass:
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
setup_result = await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert setup_result
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
@@ -1,18 +1,27 @@
|
||||
"""conftest for the GitHub integration."""
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from aiogithubapi import (
|
||||
GitHubLoginDeviceModel,
|
||||
GitHubLoginOauthModel,
|
||||
GitHubRateLimitModel,
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.github.const import CONF_REPOSITORIES, DOMAIN
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import MOCK_ACCESS_TOKEN, TEST_REPOSITORY, setup_github_integration
|
||||
from .const import MOCK_ACCESS_TOKEN, TEST_REPOSITORY
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_load_json_object_fixture,
|
||||
load_json_object_fixture,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -34,11 +43,93 @@ def mock_setup_entry() -> Generator[None]:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
def device_activation_event() -> asyncio.Event:
|
||||
"""Fixture to provide an asyncio event for device activation."""
|
||||
return asyncio.Event()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def github_device_client(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the GitHub integration for testing."""
|
||||
await setup_github_integration(hass, mock_config_entry, aioclient_mock)
|
||||
return mock_config_entry
|
||||
device_activation_event: asyncio.Event,
|
||||
) -> Generator[AsyncMock]:
|
||||
"""Mock GitHub device client."""
|
||||
with patch(
|
||||
"homeassistant.components.github.config_flow.GitHubDeviceAPI",
|
||||
autospec=True,
|
||||
) as github_client_mock:
|
||||
client = github_client_mock.return_value
|
||||
register_object = AsyncMock()
|
||||
register_object.data = GitHubLoginDeviceModel(
|
||||
load_json_object_fixture("device_register.json", DOMAIN)
|
||||
)
|
||||
client.register.return_value = register_object
|
||||
|
||||
async def mock_api_device_activation(device_code) -> AsyncMock:
|
||||
# Simulate the device activation process
|
||||
await device_activation_event.wait()
|
||||
activate_object = AsyncMock()
|
||||
activate_object.data = GitHubLoginOauthModel(
|
||||
await async_load_json_object_fixture(
|
||||
hass, "device_activate.json", DOMAIN
|
||||
)
|
||||
)
|
||||
return activate_object
|
||||
|
||||
client.activation = mock_api_device_activation
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def github_client(hass: HomeAssistant) -> Generator[AsyncMock]:
|
||||
"""Mock GitHub device client."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.github.config_flow.GitHubAPI",
|
||||
autospec=True,
|
||||
) as github_client_mock,
|
||||
patch("homeassistant.components.github.GitHubAPI", new=github_client_mock),
|
||||
patch(
|
||||
"homeassistant.components.github.diagnostics.GitHubAPI",
|
||||
new=github_client_mock,
|
||||
),
|
||||
):
|
||||
client = github_client_mock.return_value
|
||||
client.user.starred = AsyncMock(
|
||||
side_effect=[
|
||||
MagicMock(
|
||||
is_last_page=False,
|
||||
next_page_number=2,
|
||||
last_page_number=2,
|
||||
data=[MagicMock(full_name="home-assistant/core")],
|
||||
),
|
||||
MagicMock(
|
||||
is_last_page=True,
|
||||
data=[MagicMock(full_name="home-assistant/frontend")],
|
||||
),
|
||||
]
|
||||
)
|
||||
client.user.repos = AsyncMock(
|
||||
side_effect=[
|
||||
MagicMock(
|
||||
is_last_page=False,
|
||||
next_page_number=2,
|
||||
last_page_number=2,
|
||||
data=[MagicMock(full_name="home-assistant/operating-system")],
|
||||
),
|
||||
MagicMock(
|
||||
is_last_page=True,
|
||||
data=[MagicMock(full_name="esphome/esphome")],
|
||||
),
|
||||
]
|
||||
)
|
||||
rate_limit_mock = AsyncMock()
|
||||
rate_limit_mock.data = GitHubRateLimitModel(
|
||||
load_json_object_fixture("rate_limit.json", DOMAIN)
|
||||
)
|
||||
client.rate_limit.return_value = rate_limit_mock
|
||||
graphql_mock = AsyncMock()
|
||||
graphql_mock.data = load_json_object_fixture("graphql.json", DOMAIN)
|
||||
client.graphql.return_value = graphql_mock
|
||||
client.repos.events.subscribe = AsyncMock()
|
||||
yield client
|
||||
|
||||
4
tests/components/github/const.py
Normal file
4
tests/components/github/const.py
Normal file
@@ -0,0 +1,4 @@
|
||||
"""Constants for GitHub integration tests."""
|
||||
|
||||
MOCK_ACCESS_TOKEN = "gho_16C7e42F292c6912E7710c838347Ae178B4a"
|
||||
TEST_REPOSITORY = "octocat/Hello-World"
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"Server": "GitHub.com",
|
||||
"Date": "Mon, 1 Jan 1970 00:00:00 GMT",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"Transfer-Encoding": "chunked",
|
||||
"Cache-Control": "private, max-age=60, s-maxage=60",
|
||||
"Vary": "Accept, Authorization, Cookie, X-GitHub-OTP",
|
||||
"Etag": "W/\"1234567890abcdefghijklmnopqrstuvwxyz\"",
|
||||
"X-OAuth-Scopes": "",
|
||||
"X-Accepted-OAuth-Scopes": "",
|
||||
"github-authentication-token-expiration": "1970-01-01 01:00:00 UTC",
|
||||
"X-GitHub-Media-Type": "github.v3; param=raw; format=json",
|
||||
"X-RateLimit-Limit": "5000",
|
||||
"X-RateLimit-Remaining": "4999",
|
||||
"X-RateLimit-Reset": "1",
|
||||
"X-RateLimit-Used": "1",
|
||||
"X-RateLimit-Resource": "core",
|
||||
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
|
||||
"X-Frame-Options": "deny",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-XSS-Protection": "0",
|
||||
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
|
||||
"Content-Security-Policy": "default-src 'none'",
|
||||
"Content-Encoding": "gzip",
|
||||
"Permissions-Policy": "",
|
||||
"X-GitHub-Request-Id": "12A3:45BC:6D7890:12EF34:5678G901"
|
||||
}
|
||||
5
tests/components/github/fixtures/device_activate.json
Normal file
5
tests/components/github/fixtures/device_activate.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
|
||||
"token_type": "bearer",
|
||||
"scope": ""
|
||||
}
|
||||
7
tests/components/github/fixtures/device_register.json
Normal file
7
tests/components/github/fixtures/device_register.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"device_code": "3584d83530557fdd1f46af8289938c8ef79f9dc5",
|
||||
"user_code": "WDJB-MJHT",
|
||||
"verification_uri": "https://github.com/login/device",
|
||||
"expires_in": 900,
|
||||
"interval": 5
|
||||
}
|
||||
1
tests/components/github/fixtures/rate_limit.json
Normal file
1
tests/components/github/fixtures/rate_limit.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "resources": { "core": { "remaining": 100, "limit": 100 } } }
|
||||
@@ -1,146 +1,100 @@
|
||||
"""Test the GitHub config flow."""
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import asyncio
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from aiogithubapi import GitHubException
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.github.config_flow import get_repositories
|
||||
from homeassistant.components.github.const import (
|
||||
CONF_REPOSITORIES,
|
||||
DEFAULT_REPOSITORIES,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType, UnknownFlow
|
||||
|
||||
from .common import MOCK_ACCESS_TOKEN
|
||||
from .const import MOCK_ACCESS_TOKEN
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
async def test_full_user_flow_implementation(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: None,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
github_device_client: AsyncMock,
|
||||
github_client: AsyncMock,
|
||||
device_activation_event: asyncio.Event,
|
||||
) -> None:
|
||||
"""Test the full manual user flow from start to finish."""
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/device/code",
|
||||
json={
|
||||
"device_code": "3584d83530557fdd1f46af8289938c8ef79f9dc5",
|
||||
"user_code": "WDJB-MJHT",
|
||||
"verification_uri": "https://github.com/login/device",
|
||||
"expires_in": 900,
|
||||
"interval": 5,
|
||||
},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
# User has not yet entered the code
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/oauth/access_token",
|
||||
json={"error": "authorization_pending"},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["step_id"] == "device"
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
|
||||
# User enters the code
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/oauth/access_token",
|
||||
json={
|
||||
CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN,
|
||||
"token_type": "bearer",
|
||||
"scope": "",
|
||||
},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
freezer.tick(10)
|
||||
device_activation_event.set()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["step_id"] == "repositories"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert not result["errors"]
|
||||
|
||||
schema = result["data_schema"]
|
||||
repositories = schema.schema[CONF_REPOSITORIES].options
|
||||
assert len(repositories) == 4
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_REPOSITORIES: DEFAULT_REPOSITORIES,
|
||||
},
|
||||
result["flow_id"], user_input={CONF_REPOSITORIES: DEFAULT_REPOSITORIES}
|
||||
)
|
||||
|
||||
assert result["title"] == ""
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert "data" in result
|
||||
assert result["data"][CONF_ACCESS_TOKEN] == MOCK_ACCESS_TOKEN
|
||||
assert "options" in result
|
||||
assert result["options"][CONF_REPOSITORIES] == DEFAULT_REPOSITORIES
|
||||
assert result["data"] == {CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN}
|
||||
assert result["options"] == {CONF_REPOSITORIES: DEFAULT_REPOSITORIES}
|
||||
|
||||
|
||||
async def test_flow_with_registration_failure(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
github_device_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test flow with registration failure of the device."""
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/device/code",
|
||||
exc=GitHubException("Registration failed"),
|
||||
)
|
||||
github_device_client.register.side_effect = GitHubException("Registration failed")
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result.get("reason") == "could_not_register"
|
||||
assert result["reason"] == "could_not_register"
|
||||
|
||||
|
||||
async def test_flow_with_activation_failure(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
github_device_client: AsyncMock,
|
||||
device_activation_event: asyncio.Event,
|
||||
) -> None:
|
||||
"""Test flow with activation failure of the device."""
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/device/code",
|
||||
json={
|
||||
"device_code": "3584d83530557fdd1f46af8289938c8ef79f9dc5",
|
||||
"user_code": "WDJB-MJHT",
|
||||
"verification_uri": "https://github.com/login/device",
|
||||
"expires_in": 900,
|
||||
"interval": 5,
|
||||
},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
# User has not yet entered the code
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/oauth/access_token",
|
||||
json={"error": "authorization_pending"},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
async def mock_api_device_activation(device_code) -> None:
|
||||
# Simulate the device activation process
|
||||
await device_activation_event.wait()
|
||||
raise GitHubException("Activation failed")
|
||||
|
||||
github_device_client.activation = mock_api_device_activation
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["step_id"] == "device"
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
|
||||
# Activation fails
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/oauth/access_token",
|
||||
exc=GitHubException("Activation failed"),
|
||||
)
|
||||
freezer.tick(10)
|
||||
device_activation_event.set()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
@@ -149,30 +103,14 @@ async def test_flow_with_activation_failure(
|
||||
|
||||
|
||||
async def test_flow_with_remove_while_activating(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
hass: HomeAssistant, github_device_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test flow with user canceling while activating."""
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/device/code",
|
||||
json={
|
||||
"device_code": "3584d83530557fdd1f46af8289938c8ef79f9dc5",
|
||||
"user_code": "WDJB-MJHT",
|
||||
"verification_uri": "https://github.com/login/device",
|
||||
"expires_in": 900,
|
||||
"interval": 5,
|
||||
},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
aioclient_mock.post(
|
||||
"https://github.com/login/oauth/access_token",
|
||||
json={"error": "authorization_pending"},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["step_id"] == "device"
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
|
||||
@@ -194,84 +132,88 @@ async def test_already_configured(
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result.get("reason") == "already_configured"
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_starred_pagination_with_paginated_result(hass: HomeAssistant) -> None:
|
||||
"""Test pagination of starred repositories with paginated result."""
|
||||
with patch(
|
||||
"homeassistant.components.github.config_flow.GitHubAPI",
|
||||
return_value=MagicMock(
|
||||
user=MagicMock(
|
||||
starred=AsyncMock(
|
||||
return_value=MagicMock(
|
||||
is_last_page=False,
|
||||
next_page_number=2,
|
||||
last_page_number=2,
|
||||
data=[MagicMock(full_name="home-assistant/core")],
|
||||
)
|
||||
),
|
||||
repos=AsyncMock(
|
||||
return_value=MagicMock(
|
||||
is_last_page=False,
|
||||
next_page_number=2,
|
||||
last_page_number=2,
|
||||
data=[MagicMock(full_name="awesome/reposiotry")],
|
||||
)
|
||||
),
|
||||
)
|
||||
),
|
||||
):
|
||||
repos = await get_repositories(hass, MOCK_ACCESS_TOKEN)
|
||||
async def test_no_repositories(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: None,
|
||||
github_device_client: AsyncMock,
|
||||
github_client: AsyncMock,
|
||||
device_activation_event: asyncio.Event,
|
||||
) -> None:
|
||||
"""Test the full manual user flow from start to finish."""
|
||||
|
||||
assert len(repos) == 2
|
||||
assert repos[-1] == DEFAULT_REPOSITORIES[0]
|
||||
github_client.user.repos.side_effect = [MagicMock(is_last_page=True, data=[])]
|
||||
github_client.user.starred.side_effect = [MagicMock(is_last_page=True, data=[])]
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["step_id"] == "device"
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
|
||||
device_activation_event.set()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["step_id"] == "repositories"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert not result["errors"]
|
||||
|
||||
schema = result["data_schema"]
|
||||
repositories = schema.schema[CONF_REPOSITORIES].options
|
||||
assert len(repositories) == 2
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_REPOSITORIES: DEFAULT_REPOSITORIES}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_starred_pagination_with_no_starred(hass: HomeAssistant) -> None:
|
||||
"""Test pagination of starred repositories with no starred."""
|
||||
with patch(
|
||||
"homeassistant.components.github.config_flow.GitHubAPI",
|
||||
return_value=MagicMock(
|
||||
user=MagicMock(
|
||||
starred=AsyncMock(
|
||||
return_value=MagicMock(
|
||||
is_last_page=True,
|
||||
data=[],
|
||||
)
|
||||
),
|
||||
repos=AsyncMock(
|
||||
return_value=MagicMock(
|
||||
is_last_page=True,
|
||||
data=[],
|
||||
)
|
||||
),
|
||||
)
|
||||
),
|
||||
):
|
||||
repos = await get_repositories(hass, MOCK_ACCESS_TOKEN)
|
||||
async def test_exception_during_repository_fetch(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: None,
|
||||
github_device_client: AsyncMock,
|
||||
github_client: AsyncMock,
|
||||
device_activation_event: asyncio.Event,
|
||||
) -> None:
|
||||
"""Test the full manual user flow from start to finish."""
|
||||
|
||||
assert len(repos) == 2
|
||||
assert repos == DEFAULT_REPOSITORIES
|
||||
github_client.user.repos.side_effect = GitHubException()
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
async def test_starred_pagination_with_exception(hass: HomeAssistant) -> None:
|
||||
"""Test pagination of starred repositories with exception."""
|
||||
with patch(
|
||||
"homeassistant.components.github.config_flow.GitHubAPI",
|
||||
return_value=MagicMock(
|
||||
user=MagicMock(starred=AsyncMock(side_effect=GitHubException("Error")))
|
||||
),
|
||||
):
|
||||
repos = await get_repositories(hass, MOCK_ACCESS_TOKEN)
|
||||
assert result["step_id"] == "device"
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
|
||||
assert len(repos) == 2
|
||||
assert repos == DEFAULT_REPOSITORIES
|
||||
device_activation_event.set()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["step_id"] == "repositories"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert not result["errors"]
|
||||
|
||||
schema = result["data_schema"]
|
||||
repositories = schema.schema[CONF_REPOSITORIES].options
|
||||
assert len(repositories) == 2
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_REPOSITORIES: DEFAULT_REPOSITORIES}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_options_flow(
|
||||
|
||||
@@ -1,89 +1,56 @@
|
||||
"""Test GitHub diagnostics."""
|
||||
|
||||
import json
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from aiogithubapi import GitHubException
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.github.const import CONF_REPOSITORIES, DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import setup_github_integration
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, async_load_fixture
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
github_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test config entry diagnostics."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
hass.config_entries.async_update_entry(
|
||||
mock_config_entry,
|
||||
options={CONF_REPOSITORIES: ["home-assistant/core"]},
|
||||
)
|
||||
response_json = json.loads(await async_load_fixture(hass, "graphql.json", DOMAIN))
|
||||
response_json["data"]["repository"]["full_name"] = "home-assistant/core"
|
||||
|
||||
aioclient_mock.post(
|
||||
"https://api.github.com/graphql",
|
||||
json=response_json,
|
||||
headers=json.loads(await async_load_fixture(hass, "base_headers.json", DOMAIN)),
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://api.github.com/rate_limit",
|
||||
json={"resources": {"core": {"remaining": 100, "limit": 100}}},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
await setup_github_integration(
|
||||
hass, mock_config_entry, aioclient_mock, add_entry_to_hass=False
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
result = await get_diagnostics_for_config_entry(
|
||||
hass,
|
||||
hass_client,
|
||||
mock_config_entry,
|
||||
)
|
||||
|
||||
assert result["options"]["repositories"] == ["home-assistant/core"]
|
||||
assert result["options"]["repositories"] == ["octocat/Hello-World"]
|
||||
assert result["rate_limit"] == {
|
||||
"resources": {"core": {"remaining": 100, "limit": 100}}
|
||||
}
|
||||
assert (
|
||||
result["repositories"]["home-assistant/core"]["full_name"]
|
||||
== "home-assistant/core"
|
||||
result["repositories"]["octocat/Hello-World"]["full_name"]
|
||||
== "octocat/Hello-World"
|
||||
)
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_entry_diagnostics_exception(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
init_integration: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
github_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test config entry diagnostics with exception for ratelimit."""
|
||||
aioclient_mock.get(
|
||||
"https://api.github.com/rate_limit",
|
||||
exc=GitHubException("error"),
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
github_client.rate_limit.side_effect = GitHubException("error")
|
||||
|
||||
result = await get_diagnostics_for_config_entry(
|
||||
hass,
|
||||
hass_client,
|
||||
init_integration,
|
||||
mock_config_entry,
|
||||
)
|
||||
|
||||
assert (
|
||||
result["rate_limit"]["error"]
|
||||
== "Unexpected exception for 'https://api.github.com/rate_limit' with - error"
|
||||
)
|
||||
assert result["rate_limit"]["error"] == "error"
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
"""Test the GitHub init file."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.github import CONF_REPOSITORIES
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er, icon
|
||||
|
||||
from .common import setup_github_integration
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_device_registry_cleanup(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
github_client: AsyncMock,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test that we remove untracked repositories from the device registry."""
|
||||
@@ -27,9 +26,7 @@ async def test_device_registry_cleanup(
|
||||
mock_config_entry,
|
||||
options={CONF_REPOSITORIES: ["home-assistant/core"]},
|
||||
)
|
||||
await setup_github_integration(
|
||||
hass, mock_config_entry, aioclient_mock, add_entry_to_hass=False
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
devices = dr.async_entries_for_config_entry(
|
||||
registry=device_registry,
|
||||
@@ -58,12 +55,10 @@ async def test_device_registry_cleanup(
|
||||
assert len(devices) == 0
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_subscription_setup(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
github_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that we setup event subscription."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
@@ -72,21 +67,14 @@ async def test_subscription_setup(
|
||||
options={CONF_REPOSITORIES: ["home-assistant/core"]},
|
||||
pref_disable_polling=False,
|
||||
)
|
||||
await setup_github_integration(
|
||||
hass, mock_config_entry, aioclient_mock, add_entry_to_hass=False
|
||||
)
|
||||
assert (
|
||||
"https://api.github.com/repos/home-assistant/core/events" in x[1]
|
||||
for x in aioclient_mock.mock_calls
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
github_client.repos.events.subscribe.assert_called_once()
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_subscription_setup_polling_disabled(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
github_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that we do not setup event subscription if polling is disabled."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
@@ -95,13 +83,8 @@ async def test_subscription_setup_polling_disabled(
|
||||
options={CONF_REPOSITORIES: ["home-assistant/core"]},
|
||||
pref_disable_polling=True,
|
||||
)
|
||||
await setup_github_integration(
|
||||
hass, mock_config_entry, aioclient_mock, add_entry_to_hass=False
|
||||
)
|
||||
assert (
|
||||
"https://api.github.com/repos/home-assistant/core/events" not in x[1]
|
||||
for x in aioclient_mock.mock_calls
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
github_client.repos.events.subscribe.assert_not_called()
|
||||
|
||||
# Prove that we subscribed if the user enabled polling again
|
||||
hass.config_entries.async_update_entry(
|
||||
@@ -109,23 +92,20 @@ async def test_subscription_setup_polling_disabled(
|
||||
)
|
||||
assert await hass.config_entries.async_reload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert (
|
||||
"https://api.github.com/repos/home-assistant/core/events" in x[1]
|
||||
for x in aioclient_mock.mock_calls
|
||||
)
|
||||
github_client.repos.events.subscribe.assert_called_once()
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_sensor_icons(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
github_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test to ensure that all sensor entities have an icon definition."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
entities = er.async_entries_for_config_entry(
|
||||
entity_registry,
|
||||
config_entry_id=init_integration.entry_id,
|
||||
config_entry_id=mock_config_entry.entry_id,
|
||||
)
|
||||
|
||||
icons = await icon.async_get_icons(hass, "entity", integrations=["github"])
|
||||
|
||||
@@ -1,50 +1,36 @@
|
||||
"""Test GitHub sensor."""
|
||||
|
||||
import json
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.github.const import DOMAIN, FALLBACK_UPDATE_INTERVAL
|
||||
from homeassistant.components.github.const import FALLBACK_UPDATE_INTERVAL
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .common import TEST_REPOSITORY
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, async_load_fixture
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
TEST_SENSOR_ENTITY = "sensor.octocat_hello_world_latest_release"
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_sensor_updates_with_empty_release_array(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
github_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the sensor updates by default GitHub sensors."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
state = hass.states.get(TEST_SENSOR_ENTITY)
|
||||
assert state.state == "v1.0.0"
|
||||
|
||||
response_json = json.loads(await async_load_fixture(hass, "graphql.json", DOMAIN))
|
||||
response_json["data"]["repository"]["release"] = None
|
||||
headers = json.loads(await async_load_fixture(hass, "base_headers.json", DOMAIN))
|
||||
github_client.graphql.return_value.data["data"]["repository"]["release"] = None
|
||||
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.get(
|
||||
f"https://api.github.com/repos/{TEST_REPOSITORY}/events",
|
||||
json=[],
|
||||
headers=headers,
|
||||
)
|
||||
aioclient_mock.post(
|
||||
"https://api.github.com/graphql",
|
||||
json=response_json,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + FALLBACK_UPDATE_INTERVAL)
|
||||
freezer.tick(FALLBACK_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
new_state = hass.states.get(TEST_SENSOR_ENTITY)
|
||||
assert new_state.state == "unavailable"
|
||||
assert new_state.state == STATE_UNAVAILABLE
|
||||
|
||||
Reference in New Issue
Block a user