Refactor Twitch tests (#114330)

Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
This commit is contained in:
Joost Lekkerkerker 2024-05-27 10:09:12 +02:00 committed by GitHub
parent 21b9a4ef2e
commit 3d2ecd6a28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 158 additions and 323 deletions

View File

@ -39,7 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
raise ConfigEntryNotReady from err raise ConfigEntryNotReady from err
access_token = entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN] access_token = entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN]
client = await Twitch( client = Twitch(
app_id=implementation.client_id, app_id=implementation.client_id,
authenticate_app=False, authenticate_app=False,
) )

View File

@ -50,7 +50,7 @@ class OAuth2FlowHandler(
self.flow_impl, self.flow_impl,
) )
client = await Twitch( client = Twitch(
app_id=implementation.client_id, app_id=implementation.client_id,
authenticate_app=False, authenticate_app=False,
) )

View File

@ -1,246 +1,55 @@
"""Tests for the Twitch component.""" """Tests for the Twitch component."""
import asyncio
from collections.abc import AsyncGenerator, AsyncIterator from collections.abc import AsyncGenerator, AsyncIterator
from dataclasses import dataclass from typing import Any, Generic, TypeVar
from datetime import datetime
from twitchAPI.object.api import FollowedChannelsResult, TwitchUser from twitchAPI.object.base import TwitchObject
from twitchAPI.twitch import (
InvalidTokenException,
MissingScopeException,
TwitchAPIException,
TwitchAuthorizationException,
TwitchResourceNotFound,
)
from twitchAPI.type import AuthScope, AuthType
from homeassistant.components.twitch import DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, load_json_array_fixture
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Fixture for setting up the component.""" """Fixture for setting up the component."""
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
def _get_twitch_user(user_id: str = "123") -> TwitchUser: TwitchType = TypeVar("TwitchType", bound=TwitchObject)
return TwitchUser(
id=user_id,
display_name="channel123",
offline_image_url="logo.png",
profile_image_url="logo.png",
view_count=42,
)
async def async_iterator(iterable) -> AsyncIterator: class TwitchIterObject(Generic[TwitchType]):
"""Return async iterator.""" """Twitch object iterator."""
for i in iterable:
yield i
def __init__(self, fixture: str, target_type: type[TwitchType]) -> None:
"""Initialize object."""
self.raw_data = load_json_array_fixture(fixture, DOMAIN)
self.data = [target_type(**item) for item in self.raw_data]
self.total = len(self.raw_data)
self.target_type = target_type
@dataclass async def __aiter__(self) -> AsyncIterator[TwitchType]:
class UserSubscriptionMock:
"""User subscription mock."""
broadcaster_id: str
is_gift: bool
@dataclass
class FollowedChannelMock:
"""Followed channel mock."""
broadcaster_login: str
followed_at: str
@dataclass
class ChannelFollowerMock:
"""Channel follower mock."""
user_id: str
@dataclass
class StreamMock:
"""Stream mock."""
game_name: str
title: str
thumbnail_url: str
class TwitchUserFollowResultMock:
"""Mock for twitch user follow result."""
def __init__(self, follows: list[FollowedChannelMock]) -> None:
"""Initialize mock."""
self.total = len(follows)
self.data = follows
def __aiter__(self):
"""Return async iterator.""" """Return async iterator."""
return async_iterator(self.data) async for item in get_generator_from_data(self.raw_data, self.target_type):
yield item
class ChannelFollowersResultMock: async def get_generator(
"""Mock for twitch channel follow result.""" fixture: str, target_type: type[TwitchType]
) -> AsyncGenerator[TwitchType, None]:
def __init__(self, follows: list[ChannelFollowerMock]) -> None: """Return async generator."""
"""Initialize mock.""" data = load_json_array_fixture(fixture, DOMAIN)
self.total = len(follows) async for item in get_generator_from_data(data, target_type):
self.data = follows yield item
def __aiter__(self):
"""Return async iterator."""
return async_iterator(self.data)
STREAMS = StreamMock( async def get_generator_from_data(
game_name="Good game", title="Title", thumbnail_url="stream-medium.png" items: list[dict[str, Any]], target_type: type[TwitchType]
) ) -> AsyncGenerator[TwitchType, None]:
"""Return async generator."""
for item in items:
class TwitchMock: yield target_type(**item)
"""Mock for the twitch object."""
is_streaming = True
is_gifted = False
is_subscribed = False
is_following = True
different_user_id = False
def __await__(self):
"""Add async capabilities to the mock."""
t = asyncio.create_task(self._noop())
yield from t
return self
async def _noop(self):
"""Fake function to create task."""
async def get_users(
self, user_ids: list[str] | None = None, logins: list[str] | None = None
) -> AsyncGenerator[TwitchUser, None]:
"""Get list of mock users."""
users = [_get_twitch_user("234" if self.different_user_id else "123")]
for user in users:
yield user
def has_required_auth(
self, required_type: AuthType, required_scope: list[AuthScope]
) -> bool:
"""Return if auth required."""
return True
async def check_user_subscription(
self, broadcaster_id: str, user_id: str
) -> UserSubscriptionMock:
"""Check if the user is subscribed."""
if self.is_subscribed:
return UserSubscriptionMock(
broadcaster_id=broadcaster_id, is_gift=self.is_gifted
)
raise TwitchResourceNotFound
async def set_user_authentication(
self,
token: str,
scope: list[AuthScope],
refresh_token: str | None = None,
validate: bool = True,
) -> None:
"""Set user authentication."""
async def get_followed_channels(
self, user_id: str, broadcaster_id: str | None = None
) -> FollowedChannelsResult:
"""Get followed channels."""
if self.is_following:
return TwitchUserFollowResultMock(
[
FollowedChannelMock(
followed_at=datetime(year=2023, month=8, day=1),
broadcaster_login="internetofthings",
),
FollowedChannelMock(
followed_at=datetime(year=2023, month=8, day=1),
broadcaster_login="homeassistant",
),
]
)
return TwitchUserFollowResultMock([])
async def get_channel_followers(
self, broadcaster_id: str
) -> ChannelFollowersResultMock:
"""Get channel followers."""
return ChannelFollowersResultMock([ChannelFollowerMock(user_id="abc")])
async def get_streams(
self, user_id: list[str], first: int
) -> AsyncGenerator[StreamMock, None]:
"""Get streams for the user."""
streams = []
if self.is_streaming:
streams = [STREAMS]
for stream in streams:
yield stream
class TwitchUnauthorizedMock(TwitchMock):
"""Twitch mock to test if the client is unauthorized."""
def __await__(self):
"""Add async capabilities to the mock."""
raise TwitchAuthorizationException
class TwitchMissingScopeMock(TwitchMock):
"""Twitch mock to test missing scopes."""
async def set_user_authentication(
self, token: str, scope: list[AuthScope], validate: bool = True
) -> None:
"""Set user authentication."""
raise MissingScopeException
class TwitchInvalidTokenMock(TwitchMock):
"""Twitch mock to test invalid token."""
async def set_user_authentication(
self, token: str, scope: list[AuthScope], validate: bool = True
) -> None:
"""Set user authentication."""
raise InvalidTokenException
class TwitchInvalidUserMock(TwitchMock):
"""Twitch mock to test invalid user."""
async def get_users(
self, user_ids: list[str] | None = None, logins: list[str] | None = None
) -> AsyncGenerator[TwitchUser, None]:
"""Get list of mock users."""
if user_ids is not None or logins is not None:
async for user in super().get_users(user_ids, logins):
yield user
else:
for user in []:
yield user
class TwitchAPIExceptionMock(TwitchMock):
"""Twitch mock to test when twitch api throws unknown exception."""
async def check_user_subscription(
self, broadcaster_id: str, user_id: str
) -> UserSubscriptionMock:
"""Check if the user is subscribed."""
raise TwitchAPIException

View File

@ -1,10 +1,11 @@
"""Configure tests for the Twitch integration.""" """Configure tests for the Twitch integration."""
from collections.abc import Awaitable, Callable, Generator from collections.abc import Generator
import time import time
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from twitchAPI.object.api import FollowedChannel, Stream, TwitchUser, UserSubscription
from homeassistant.components.application_credentials import ( from homeassistant.components.application_credentials import (
ClientCredential, ClientCredential,
@ -14,11 +15,10 @@ from homeassistant.components.twitch.const import DOMAIN, OAUTH2_TOKEN, OAUTH_SC
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from . import TwitchIterObject, get_generator
from tests.components.twitch import TwitchMock
from tests.test_util.aiohttp import AiohttpClientMocker
type ComponentSetup = Callable[[TwitchMock | None], Awaitable[None]] from tests.common import MockConfigEntry, load_json_object_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
CLIENT_ID = "1234" CLIENT_ID = "1234"
CLIENT_SECRET = "5678" CLIENT_SECRET = "5678"
@ -92,23 +92,32 @@ def mock_connection(aioclient_mock: AiohttpClientMocker) -> None:
) )
@pytest.fixture(name="twitch_mock") @pytest.fixture
def twitch_mock() -> TwitchMock: def twitch_mock() -> Generator[AsyncMock, None, None]:
"""Return as fixture to inject other mocks.""" """Return as fixture to inject other mocks."""
return TwitchMock()
@pytest.fixture(name="twitch")
def mock_twitch(twitch_mock: TwitchMock):
"""Mock Twitch."""
with ( with (
patch( patch(
"homeassistant.components.twitch.Twitch", "homeassistant.components.twitch.Twitch",
return_value=twitch_mock, autospec=True,
), ) as mock_client,
patch( patch(
"homeassistant.components.twitch.config_flow.Twitch", "homeassistant.components.twitch.config_flow.Twitch",
return_value=twitch_mock, new=mock_client,
), ),
): ):
yield twitch_mock mock_client.return_value.get_users = lambda *args, **kwargs: get_generator(
"get_users.json", TwitchUser
)
mock_client.return_value.get_followed_channels.return_value = TwitchIterObject(
"get_followed_channels.json", FollowedChannel
)
mock_client.return_value.get_streams.return_value = get_generator(
"get_streams.json", Stream
)
mock_client.return_value.check_user_subscription.return_value = (
UserSubscription(
**load_json_object_fixture("check_user_subscription.json", DOMAIN)
)
)
mock_client.return_value.has_required_auth.return_value = True
yield mock_client

View File

@ -0,0 +1,3 @@
{
"is_gift": true
}

View File

@ -0,0 +1,3 @@
{
"is_gift": false
}

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1,10 @@
[
{
"broadcaster_login": "internetofthings",
"followed_at": "2023-08-01"
},
{
"broadcaster_login": "homeassistant",
"followed_at": "2023-08-01"
}
]

View File

@ -0,0 +1,7 @@
[
{
"game_name": "Good game",
"title": "Title",
"thumbnail_url": "stream-medium.png"
}
]

View File

@ -0,0 +1,9 @@
[
{
"id": 123,
"display_name": "channel123",
"offline_image_url": "logo.png",
"profile_image_url": "logo.png",
"view_count": 42
}
]

View File

@ -0,0 +1,9 @@
[
{
"id": 456,
"display_name": "channel123",
"offline_image_url": "logo.png",
"profile_image_url": "logo.png",
"view_count": 42
}
]

View File

@ -1,6 +1,8 @@
"""Test config flow for Twitch.""" """Test config flow for Twitch."""
from unittest.mock import patch from unittest.mock import AsyncMock
from twitchAPI.object.api import TwitchUser
from homeassistant.components.twitch.const import ( from homeassistant.components.twitch.const import (
CONF_CHANNELS, CONF_CHANNELS,
@ -12,10 +14,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult, FlowResultType from homeassistant.data_entry_flow import FlowResult, FlowResultType
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
from . import setup_integration from . import get_generator, setup_integration
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.components.twitch import TwitchMock
from tests.components.twitch.conftest import CLIENT_ID, TITLE from tests.components.twitch.conftest import CLIENT_ID, TITLE
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@ -51,7 +52,7 @@ async def test_full_flow(
hass_client_no_auth: ClientSessionGenerator, hass_client_no_auth: ClientSessionGenerator,
current_request_with_host: None, current_request_with_host: None,
mock_setup_entry, mock_setup_entry,
twitch: TwitchMock, twitch_mock: AsyncMock,
scopes: list[str], scopes: list[str],
) -> None: ) -> None:
"""Check full flow.""" """Check full flow."""
@ -80,7 +81,7 @@ async def test_already_configured(
current_request_with_host: None, current_request_with_host: None,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
mock_setup_entry, mock_setup_entry,
twitch: TwitchMock, twitch_mock: AsyncMock,
scopes: list[str], scopes: list[str],
) -> None: ) -> None:
"""Check flow aborts when account already configured.""" """Check flow aborts when account already configured."""
@ -90,13 +91,10 @@ async def test_already_configured(
) )
await _do_get_token(hass, result, hass_client_no_auth, scopes) await _do_get_token(hass, result, hass_client_no_auth, scopes)
with patch( result = await hass.config_entries.flow.async_configure(result["flow_id"])
"homeassistant.components.twitch.config_flow.Twitch", return_value=TwitchMock()
):
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
async def test_reauth( async def test_reauth(
@ -105,7 +103,7 @@ async def test_reauth(
current_request_with_host: None, current_request_with_host: None,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
mock_setup_entry, mock_setup_entry,
twitch: TwitchMock, twitch_mock: AsyncMock,
scopes: list[str], scopes: list[str],
) -> None: ) -> None:
"""Check reauth flow.""" """Check reauth flow."""
@ -136,7 +134,7 @@ async def test_reauth_from_import(
hass_client_no_auth: ClientSessionGenerator, hass_client_no_auth: ClientSessionGenerator,
current_request_with_host: None, current_request_with_host: None,
mock_setup_entry, mock_setup_entry,
twitch: TwitchMock, twitch_mock: AsyncMock,
expires_at, expires_at,
scopes: list[str], scopes: list[str],
) -> None: ) -> None:
@ -163,7 +161,7 @@ async def test_reauth_from_import(
current_request_with_host, current_request_with_host,
config_entry, config_entry,
mock_setup_entry, mock_setup_entry,
twitch, twitch_mock,
scopes, scopes,
) )
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
@ -178,12 +176,14 @@ async def test_reauth_wrong_account(
current_request_with_host: None, current_request_with_host: None,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
mock_setup_entry, mock_setup_entry,
twitch: TwitchMock, twitch_mock: AsyncMock,
scopes: list[str], scopes: list[str],
) -> None: ) -> None:
"""Check reauth flow.""" """Check reauth flow."""
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
twitch.different_user_id = True twitch_mock.return_value.get_users = lambda *args, **kwargs: get_generator(
"get_users_2.json", TwitchUser
)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={ context={

View File

@ -1,8 +1,8 @@
"""Tests for YouTube.""" """Tests for Twitch."""
import http import http
import time import time
from unittest.mock import patch from unittest.mock import AsyncMock, patch
from aiohttp.client_exceptions import ClientError from aiohttp.client_exceptions import ClientError
import pytest import pytest
@ -11,14 +11,14 @@ from homeassistant.components.twitch.const import DOMAIN, OAUTH2_TOKEN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import TwitchMock, setup_integration from . import setup_integration
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
async def test_setup_success( async def test_setup_success(
hass: HomeAssistant, config_entry: MockConfigEntry, twitch: TwitchMock hass: HomeAssistant, config_entry: MockConfigEntry, twitch_mock: AsyncMock
) -> None: ) -> None:
"""Test successful setup and unload.""" """Test successful setup and unload."""
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
@ -38,7 +38,7 @@ async def test_expired_token_refresh_success(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
twitch: TwitchMock, twitch_mock: AsyncMock,
) -> None: ) -> None:
"""Test expired token is refreshed.""" """Test expired token is refreshed."""
@ -84,7 +84,7 @@ async def test_expired_token_refresh_failure(
status: http.HTTPStatus, status: http.HTTPStatus,
expected_state: ConfigEntryState, expected_state: ConfigEntryState,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
twitch: TwitchMock, twitch_mock: AsyncMock,
) -> None: ) -> None:
"""Test failure while refreshing token with a transient error.""" """Test failure while refreshing token with a transient error."""
@ -93,8 +93,10 @@ async def test_expired_token_refresh_failure(
OAUTH2_TOKEN, OAUTH2_TOKEN,
status=status, status=status,
) )
config_entry.add_to_hass(hass)
await setup_integration(hass, config_entry) assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
# Verify a transient failure has occurred # Verify a transient failure has occurred
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
@ -102,7 +104,7 @@ async def test_expired_token_refresh_failure(
async def test_expired_token_refresh_client_error( async def test_expired_token_refresh_client_error(
hass: HomeAssistant, config_entry: MockConfigEntry, twitch: TwitchMock hass: HomeAssistant, config_entry: MockConfigEntry, twitch_mock: AsyncMock
) -> None: ) -> None:
"""Test failure while refreshing token with a client error.""" """Test failure while refreshing token with a client error."""
@ -110,7 +112,10 @@ async def test_expired_token_refresh_client_error(
"homeassistant.components.twitch.OAuth2Session.async_ensure_token_valid", "homeassistant.components.twitch.OAuth2Session.async_ensure_token_valid",
side_effect=ClientError, side_effect=ClientError,
): ):
await setup_integration(hass, config_entry) config_entry.add_to_hass(hass)
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
# Verify a transient failure has occurred # Verify a transient failure has occurred
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)

View File

@ -1,30 +1,28 @@
"""The tests for an update of the Twitch component.""" """The tests for an update of the Twitch component."""
from datetime import datetime from datetime import datetime
from unittest.mock import AsyncMock
import pytest from twitchAPI.object.api import FollowedChannel, Stream, UserSubscription
from twitchAPI.type import TwitchResourceNotFound
from homeassistant.components.twitch import DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from ...common import MockConfigEntry from . import TwitchIterObject, get_generator_from_data, setup_integration
from . import (
TwitchAPIExceptionMock, from tests.common import MockConfigEntry, load_json_object_fixture
TwitchInvalidTokenMock,
TwitchInvalidUserMock,
TwitchMissingScopeMock,
TwitchMock,
TwitchUnauthorizedMock,
setup_integration,
)
ENTITY_ID = "sensor.channel123" ENTITY_ID = "sensor.channel123"
async def test_offline( async def test_offline(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry hass: HomeAssistant, twitch_mock: AsyncMock, config_entry: MockConfigEntry
) -> None: ) -> None:
"""Test offline state.""" """Test offline state."""
twitch.is_streaming = False twitch_mock.return_value.get_streams.return_value = get_generator_from_data(
[], Stream
)
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
sensor_state = hass.states.get(ENTITY_ID) sensor_state = hass.states.get(ENTITY_ID)
@ -33,7 +31,7 @@ async def test_offline(
async def test_streaming( async def test_streaming(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry hass: HomeAssistant, twitch_mock: AsyncMock, config_entry: MockConfigEntry
) -> None: ) -> None:
"""Test streaming state.""" """Test streaming state."""
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
@ -46,10 +44,15 @@ async def test_streaming(
async def test_oauth_without_sub_and_follow( async def test_oauth_without_sub_and_follow(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry hass: HomeAssistant, twitch_mock: AsyncMock, config_entry: MockConfigEntry
) -> None: ) -> None:
"""Test state with oauth.""" """Test state with oauth."""
twitch.is_following = False twitch_mock.return_value.get_followed_channels.return_value = TwitchIterObject(
"empty_response.json", FollowedChannel
)
twitch_mock.return_value.check_user_subscription.side_effect = (
TwitchResourceNotFound
)
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
sensor_state = hass.states.get(ENTITY_ID) sensor_state = hass.states.get(ENTITY_ID)
@ -58,11 +61,15 @@ async def test_oauth_without_sub_and_follow(
async def test_oauth_with_sub( async def test_oauth_with_sub(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry hass: HomeAssistant, twitch_mock: AsyncMock, config_entry: MockConfigEntry
) -> None: ) -> None:
"""Test state with oauth and sub.""" """Test state with oauth and sub."""
twitch.is_subscribed = True twitch_mock.return_value.get_followed_channels.return_value = TwitchIterObject(
twitch.is_following = False "empty_response.json", FollowedChannel
)
twitch_mock.return_value.check_user_subscription.return_value = UserSubscription(
**load_json_object_fixture("check_user_subscription_2.json", DOMAIN)
)
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
sensor_state = hass.states.get(ENTITY_ID) sensor_state = hass.states.get(ENTITY_ID)
@ -72,7 +79,7 @@ async def test_oauth_with_sub(
async def test_oauth_with_follow( async def test_oauth_with_follow(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry hass: HomeAssistant, twitch_mock: AsyncMock, config_entry: MockConfigEntry
) -> None: ) -> None:
"""Test state with oauth and follow.""" """Test state with oauth and follow."""
await setup_integration(hass, config_entry) await setup_integration(hass, config_entry)
@ -82,40 +89,3 @@ async def test_oauth_with_follow(
assert sensor_state.attributes["following_since"] == datetime( assert sensor_state.attributes["following_since"] == datetime(
year=2023, month=8, day=1 year=2023, month=8, day=1
) )
@pytest.mark.parametrize(
"twitch_mock",
[TwitchUnauthorizedMock(), TwitchMissingScopeMock(), TwitchInvalidTokenMock()],
)
async def test_auth_invalid(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry
) -> None:
"""Test auth failures."""
await setup_integration(hass, config_entry)
sensor_state = hass.states.get(ENTITY_ID)
assert sensor_state is None
@pytest.mark.parametrize("twitch_mock", [TwitchInvalidUserMock()])
async def test_auth_with_invalid_user(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry
) -> None:
"""Test auth with invalid user."""
await setup_integration(hass, config_entry)
sensor_state = hass.states.get(ENTITY_ID)
assert "subscribed" not in sensor_state.attributes
@pytest.mark.parametrize("twitch_mock", [TwitchAPIExceptionMock()])
async def test_auth_with_api_exception(
hass: HomeAssistant, twitch: TwitchMock, config_entry: MockConfigEntry
) -> None:
"""Test auth with invalid user."""
await setup_integration(hass, config_entry)
sensor_state = hass.states.get(ENTITY_ID)
assert sensor_state.attributes["subscribed"] is False
assert "subscription_is_gifted" not in sensor_state.attributes