Plex external config flow (#26936)
* Plex external auth config flow * Update requirements_all * Test dependency * Bad await, delay variable * Use hass aiohttp session, bump plexauth * Bump requirements * Bump library version again * Use callback view instead of polling * Update tests for callback view * Reduce timeout with callback * Review feedback * F-string * Wrap sync call * Unused * Revert unnecessary async wrap
This commit is contained in:
parent
c1851a2d94
commit
571ab5a978
10 changed files with 267 additions and 77 deletions
|
@ -2,10 +2,14 @@
|
|||
import copy
|
||||
import logging
|
||||
|
||||
from aiohttp import web_response
|
||||
import plexapi.exceptions
|
||||
from plexauth import PlexAuth
|
||||
import requests.exceptions
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.http.view import HomeAssistantView
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.const import (
|
||||
|
@ -20,6 +24,8 @@ from homeassistant.core import callback
|
|||
from homeassistant.util.json import load_json
|
||||
|
||||
from .const import ( # pylint: disable=unused-import
|
||||
AUTH_CALLBACK_NAME,
|
||||
AUTH_CALLBACK_PATH,
|
||||
CONF_SERVER,
|
||||
CONF_SERVER_IDENTIFIER,
|
||||
CONF_USE_EPISODE_ART,
|
||||
|
@ -30,13 +36,15 @@ from .const import ( # pylint: disable=unused-import
|
|||
DOMAIN,
|
||||
PLEX_CONFIG_FILE,
|
||||
PLEX_SERVER_CONFIG,
|
||||
X_PLEX_DEVICE_NAME,
|
||||
X_PLEX_VERSION,
|
||||
X_PLEX_PRODUCT,
|
||||
X_PLEX_PLATFORM,
|
||||
)
|
||||
from .errors import NoServersFound, ServerNotSpecified
|
||||
from .server import PlexServer
|
||||
|
||||
USER_SCHEMA = vol.Schema(
|
||||
{vol.Optional(CONF_TOKEN): str, vol.Optional("manual_setup"): bool}
|
||||
)
|
||||
USER_SCHEMA = vol.Schema({vol.Optional("manual_setup"): bool})
|
||||
|
||||
_LOGGER = logging.getLogger(__package__)
|
||||
|
||||
|
@ -67,6 +75,8 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
self.current_login = {}
|
||||
self.discovery_info = {}
|
||||
self.available_servers = None
|
||||
self.plexauth = None
|
||||
self.token = None
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow initialized by the user."""
|
||||
|
@ -74,9 +84,8 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
if user_input is not None:
|
||||
if user_input.pop("manual_setup", False):
|
||||
return await self.async_step_manual_setup(user_input)
|
||||
if CONF_TOKEN in user_input:
|
||||
return await self.async_step_server_validate(user_input)
|
||||
errors[CONF_TOKEN] = "no_token"
|
||||
|
||||
return await self.async_step_plex_website_auth()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=USER_SCHEMA, errors=errors
|
||||
|
@ -225,6 +234,43 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
_LOGGER.debug("Imported Plex configuration")
|
||||
return await self.async_step_server_validate(import_config)
|
||||
|
||||
async def async_step_plex_website_auth(self):
|
||||
"""Begin external auth flow on Plex website."""
|
||||
self.hass.http.register_view(PlexAuthorizationCallbackView)
|
||||
payload = {
|
||||
"X-Plex-Device-Name": X_PLEX_DEVICE_NAME,
|
||||
"X-Plex-Version": X_PLEX_VERSION,
|
||||
"X-Plex-Product": X_PLEX_PRODUCT,
|
||||
"X-Plex-Device": self.hass.config.location_name,
|
||||
"X-Plex-Platform": X_PLEX_PLATFORM,
|
||||
"X-Plex-Model": "Plex OAuth",
|
||||
}
|
||||
session = async_get_clientsession(self.hass)
|
||||
self.plexauth = PlexAuth(payload, session)
|
||||
await self.plexauth.initiate_auth()
|
||||
forward_url = f"{self.hass.config.api.base_url}{AUTH_CALLBACK_PATH}?flow_id={self.flow_id}"
|
||||
auth_url = self.plexauth.auth_url(forward_url)
|
||||
return self.async_external_step(step_id="obtain_token", url=auth_url)
|
||||
|
||||
async def async_step_obtain_token(self, user_input=None):
|
||||
"""Obtain token after external auth completed."""
|
||||
token = await self.plexauth.token(10)
|
||||
|
||||
if not token:
|
||||
return self.async_external_step_done(next_step_id="timed_out")
|
||||
|
||||
self.token = token
|
||||
return self.async_external_step_done(next_step_id="use_external_token")
|
||||
|
||||
async def async_step_timed_out(self, user_input=None):
|
||||
"""Abort flow when time expires."""
|
||||
return self.async_abort(reason="token_request_timeout")
|
||||
|
||||
async def async_step_use_external_token(self, user_input=None):
|
||||
"""Continue server validation with external token."""
|
||||
server_config = {CONF_TOKEN: self.token}
|
||||
return await self.async_step_server_validate(server_config)
|
||||
|
||||
|
||||
class PlexOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle Plex options."""
|
||||
|
@ -263,3 +309,23 @@ class PlexOptionsFlowHandler(config_entries.OptionsFlow):
|
|||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class PlexAuthorizationCallbackView(HomeAssistantView):
|
||||
"""Handle callback from external auth."""
|
||||
|
||||
url = AUTH_CALLBACK_PATH
|
||||
name = AUTH_CALLBACK_NAME
|
||||
requires_auth = False
|
||||
|
||||
async def get(self, request):
|
||||
"""Receive authorization confirmation."""
|
||||
hass = request.app["hass"]
|
||||
await hass.config_entries.flow.async_configure(
|
||||
flow_id=request.query["flow_id"], user_input=None
|
||||
)
|
||||
|
||||
return web_response.Response(
|
||||
headers={"content-type": "text/html"},
|
||||
text="<script>window.close()</script>Success! This window can be closed",
|
||||
)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Constants for the Plex component."""
|
||||
from homeassistant.const import __version__
|
||||
|
||||
DOMAIN = "plex"
|
||||
NAME_FORMAT = "Plex {}"
|
||||
|
||||
|
@ -18,3 +20,11 @@ CONF_SERVER = "server"
|
|||
CONF_SERVER_IDENTIFIER = "server_id"
|
||||
CONF_USE_EPISODE_ART = "use_episode_art"
|
||||
CONF_SHOW_ALL_CONTROLS = "show_all_controls"
|
||||
|
||||
AUTH_CALLBACK_PATH = "/auth/plex/callback"
|
||||
AUTH_CALLBACK_NAME = "auth:plex:callback"
|
||||
|
||||
X_PLEX_DEVICE_NAME = "Home Assistant"
|
||||
X_PLEX_PLATFORM = "Home Assistant"
|
||||
X_PLEX_PRODUCT = "Home Assistant"
|
||||
X_PLEX_VERSION = __version__
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/components/plex",
|
||||
"requirements": [
|
||||
"plexapi==3.0.6"
|
||||
"plexapi==3.0.6",
|
||||
"plexauth==0.0.4"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": [
|
||||
|
|
|
@ -11,9 +11,21 @@ from .const import (
|
|||
CONF_SHOW_ALL_CONTROLS,
|
||||
CONF_USE_EPISODE_ART,
|
||||
DEFAULT_VERIFY_SSL,
|
||||
X_PLEX_DEVICE_NAME,
|
||||
X_PLEX_PLATFORM,
|
||||
X_PLEX_PRODUCT,
|
||||
X_PLEX_VERSION,
|
||||
)
|
||||
from .errors import NoServersFound, ServerNotSpecified
|
||||
|
||||
# Set default headers sent by plexapi
|
||||
plexapi.X_PLEX_DEVICE_NAME = X_PLEX_DEVICE_NAME
|
||||
plexapi.X_PLEX_PLATFORM = X_PLEX_PLATFORM
|
||||
plexapi.X_PLEX_PRODUCT = X_PLEX_PRODUCT
|
||||
plexapi.X_PLEX_VERSION = X_PLEX_VERSION
|
||||
plexapi.myplex.BASE_HEADERS = plexapi.reset_base_headers()
|
||||
plexapi.server.BASE_HEADERS = plexapi.reset_base_headers()
|
||||
|
||||
|
||||
class PlexServer:
|
||||
"""Manages a single Plex server connection."""
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
},
|
||||
"user": {
|
||||
"title": "Connect Plex server",
|
||||
"description": "Enter a Plex token for automatic setup or manually configure a server.",
|
||||
"description": "Continue to authorize at plex.tv or manually configure a server.",
|
||||
"data": {
|
||||
"token": "Plex token",
|
||||
"manual_setup": "Manual setup"
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +30,14 @@
|
|||
"error": {
|
||||
"faulty_credentials": "Authorization failed",
|
||||
"no_servers": "No servers linked to account",
|
||||
"not_found": "Plex server not found",
|
||||
"no_token": "Provide a token or select manual setup"
|
||||
"not_found": "Plex server not found"
|
||||
},
|
||||
"abort": {
|
||||
"all_configured": "All linked servers already configured",
|
||||
"already_configured": "This Plex server is already configured",
|
||||
"already_in_progress": "Plex is being configured",
|
||||
"invalid_import": "Imported configuration is invalid",
|
||||
"token_request_timeout": "Timed out obtaining token",
|
||||
"unknown": "Failed for unknown reason"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -964,6 +964,9 @@ pizzapi==0.0.3
|
|||
# homeassistant.components.plex
|
||||
plexapi==3.0.6
|
||||
|
||||
# homeassistant.components.plex
|
||||
plexauth==0.0.4
|
||||
|
||||
# homeassistant.components.plum_lightpad
|
||||
plumlightpad==0.0.11
|
||||
|
||||
|
|
|
@ -261,6 +261,9 @@ pillow==6.1.0
|
|||
# homeassistant.components.plex
|
||||
plexapi==3.0.6
|
||||
|
||||
# homeassistant.components.plex
|
||||
plexauth==0.0.4
|
||||
|
||||
# homeassistant.components.mhz19
|
||||
# homeassistant.components.serial_pm
|
||||
pmsensor==0.4
|
||||
|
|
|
@ -113,6 +113,7 @@ TEST_REQUIREMENTS = (
|
|||
"pilight",
|
||||
"pillow",
|
||||
"plexapi",
|
||||
"plexauth",
|
||||
"pmsensor",
|
||||
"prometheus_client",
|
||||
"ptvsd",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
"""Mock classes used in tests."""
|
||||
|
||||
MOCK_HOST_1 = "1.2.3.4"
|
||||
MOCK_PORT_1 = "32400"
|
||||
MOCK_PORT_1 = 32400
|
||||
MOCK_HOST_2 = "4.3.2.1"
|
||||
MOCK_PORT_2 = "32400"
|
||||
MOCK_PORT_2 = 32400
|
||||
|
||||
|
||||
class MockAvailableServer: # pylint: disable=too-few-public-methods
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"""Tests for Plex config flow."""
|
||||
from unittest.mock import MagicMock, Mock, patch, PropertyMock
|
||||
|
||||
import asynctest
|
||||
import plexapi.exceptions
|
||||
import requests.exceptions
|
||||
|
||||
|
@ -12,6 +14,7 @@ from homeassistant.const import (
|
|||
CONF_TOKEN,
|
||||
CONF_URL,
|
||||
)
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -49,19 +52,28 @@ async def test_bad_credentials(hass):
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch(
|
||||
"plexapi.myplex.MyPlexAccount", side_effect=plexapi.exceptions.Unauthorized
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"manual_setup": True}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "manual_setup"
|
||||
|
||||
with patch(
|
||||
"plexapi.server.PlexServer", side_effect=plexapi.exceptions.Unauthorized
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
user_input={
|
||||
CONF_HOST: MOCK_HOST_1,
|
||||
CONF_PORT: MOCK_PORT_1,
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
CONF_TOKEN: "BAD TOKEN",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"]["base"] == "faulty_credentials"
|
||||
|
@ -92,7 +104,6 @@ async def test_import_file_from_discovery(hass):
|
|||
context={"source": "discovery"},
|
||||
data={CONF_HOST: MOCK_HOST_1, CONF_PORT: MOCK_PORT_1},
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == MOCK_NAME_1
|
||||
assert result["data"][config_flow.CONF_SERVER] == MOCK_NAME_1
|
||||
|
@ -112,7 +123,6 @@ async def test_discovery(hass):
|
|||
context={"source": "discovery"},
|
||||
data={CONF_HOST: MOCK_HOST_1, CONF_PORT: MOCK_PORT_1},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
@ -129,7 +139,6 @@ async def test_discovery_while_in_progress(hass):
|
|||
context={"source": "discovery"},
|
||||
data={CONF_HOST: MOCK_HOST_1, CONF_PORT: MOCK_PORT_1},
|
||||
)
|
||||
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
@ -191,7 +200,6 @@ async def test_import_bad_hostname(hass):
|
|||
CONF_URL: f"http://{MOCK_HOST_1}:{MOCK_PORT_1}",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"]["base"] == "not_found"
|
||||
|
@ -203,15 +211,25 @@ async def test_unknown_exception(hass):
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch("plexapi.myplex.MyPlexAccount", side_effect=Exception):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN,
|
||||
context={"source": "user"},
|
||||
data={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"manual_setup": True}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "manual_setup"
|
||||
|
||||
with patch("plexapi.server.PlexServer", side_effect=Exception):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_HOST: MOCK_HOST_1,
|
||||
CONF_PORT: MOCK_PORT_1,
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: True,
|
||||
CONF_TOKEN: MOCK_TOKEN,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "abort"
|
||||
|
@ -221,23 +239,32 @@ async def test_unknown_exception(hass):
|
|||
async def test_no_servers_found(hass):
|
||||
"""Test when no servers are on an account."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
mm_plex_account = MagicMock()
|
||||
mm_plex_account.resources = Mock(return_value=[])
|
||||
|
||||
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account):
|
||||
with patch(
|
||||
"plexapi.myplex.MyPlexAccount", return_value=mm_plex_account
|
||||
), asynctest.patch("plexauth.PlexAuth.initiate_auth"), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=MOCK_TOKEN
|
||||
):
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "external_done"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"]["base"] == "no_servers"
|
||||
|
@ -246,10 +273,11 @@ async def test_no_servers_found(hass):
|
|||
async def test_single_available_server(hass):
|
||||
"""Test creating an entry with one server available."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
@ -261,7 +289,11 @@ async def test_single_available_server(hass):
|
|||
|
||||
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account), patch(
|
||||
"plexapi.server.PlexServer"
|
||||
) as mock_plex_server:
|
||||
) as mock_plex_server, asynctest.patch(
|
||||
"plexauth.PlexAuth.initiate_auth"
|
||||
), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=MOCK_TOKEN
|
||||
):
|
||||
type(mock_plex_server.return_value).machineIdentifier = PropertyMock(
|
||||
return_value=MOCK_SERVER_1.clientIdentifier
|
||||
)
|
||||
|
@ -273,10 +305,14 @@ async def test_single_available_server(hass):
|
|||
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "external_done"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == MOCK_SERVER_1.name
|
||||
assert result["data"][config_flow.CONF_SERVER] == MOCK_SERVER_1.name
|
||||
|
@ -294,10 +330,11 @@ async def test_single_available_server(hass):
|
|||
async def test_multiple_servers_with_selection(hass):
|
||||
"""Test creating an entry with multiple servers available."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
@ -308,7 +345,11 @@ async def test_multiple_servers_with_selection(hass):
|
|||
|
||||
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account), patch(
|
||||
"plexapi.server.PlexServer"
|
||||
) as mock_plex_server:
|
||||
) as mock_plex_server, asynctest.patch(
|
||||
"plexauth.PlexAuth.initiate_auth"
|
||||
), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=MOCK_TOKEN
|
||||
):
|
||||
type(mock_plex_server.return_value).machineIdentifier = PropertyMock(
|
||||
return_value=MOCK_SERVER_1.clientIdentifier
|
||||
)
|
||||
|
@ -320,17 +361,20 @@ async def test_multiple_servers_with_selection(hass):
|
|||
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "external_done"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "select_server"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={config_flow.CONF_SERVER: MOCK_SERVER_1.name}
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == MOCK_SERVER_1.name
|
||||
assert result["data"][config_flow.CONF_SERVER] == MOCK_SERVER_1.name
|
||||
|
@ -348,6 +392,8 @@ async def test_multiple_servers_with_selection(hass):
|
|||
async def test_adding_last_unconfigured_server(hass):
|
||||
"""Test automatically adding last unconfigured server when multiple servers on account."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
MockConfigEntry(
|
||||
domain=config_flow.DOMAIN,
|
||||
data={
|
||||
|
@ -359,7 +405,6 @@ async def test_adding_last_unconfigured_server(hass):
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
@ -370,7 +415,11 @@ async def test_adding_last_unconfigured_server(hass):
|
|||
|
||||
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account), patch(
|
||||
"plexapi.server.PlexServer"
|
||||
) as mock_plex_server:
|
||||
) as mock_plex_server, asynctest.patch(
|
||||
"plexauth.PlexAuth.initiate_auth"
|
||||
), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=MOCK_TOKEN
|
||||
):
|
||||
type(mock_plex_server.return_value).machineIdentifier = PropertyMock(
|
||||
return_value=MOCK_SERVER_1.clientIdentifier
|
||||
)
|
||||
|
@ -382,10 +431,14 @@ async def test_adding_last_unconfigured_server(hass):
|
|||
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "external_done"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == MOCK_SERVER_1.name
|
||||
assert result["data"][config_flow.CONF_SERVER] == MOCK_SERVER_1.name
|
||||
|
@ -414,7 +467,9 @@ async def test_already_configured(hass):
|
|||
mm_plex_account.resources = Mock(return_value=[MOCK_SERVER_1])
|
||||
mm_plex_account.resource = Mock(return_value=mock_connections)
|
||||
|
||||
with patch("plexapi.server.PlexServer") as mock_plex_server:
|
||||
with patch("plexapi.server.PlexServer") as mock_plex_server, asynctest.patch(
|
||||
"plexauth.PlexAuth.initiate_auth"
|
||||
), asynctest.patch("plexauth.PlexAuth.token", return_value=MOCK_TOKEN):
|
||||
type(mock_plex_server.return_value).machineIdentifier = PropertyMock(
|
||||
return_value=MOCK_SERVER_1.clientIdentifier
|
||||
)
|
||||
|
@ -424,10 +479,10 @@ async def test_already_configured(hass):
|
|||
type( # pylint: disable=protected-access
|
||||
mock_plex_server.return_value
|
||||
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)
|
||||
|
||||
result = await flow.async_step_import(
|
||||
{CONF_TOKEN: MOCK_TOKEN, CONF_URL: f"http://{MOCK_HOST_1}:{MOCK_PORT_1}"}
|
||||
)
|
||||
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
@ -435,6 +490,8 @@ async def test_already_configured(hass):
|
|||
async def test_all_available_servers_configured(hass):
|
||||
"""Test when all available servers are already configured."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
MockConfigEntry(
|
||||
domain=config_flow.DOMAIN,
|
||||
data={
|
||||
|
@ -454,7 +511,6 @@ async def test_all_available_servers_configured(hass):
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
@ -463,13 +519,21 @@ async def test_all_available_servers_configured(hass):
|
|||
mm_plex_account.resources = Mock(return_value=[MOCK_SERVER_1, MOCK_SERVER_2])
|
||||
mm_plex_account.resource = Mock(return_value=mock_connections)
|
||||
|
||||
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account):
|
||||
with patch(
|
||||
"plexapi.myplex.MyPlexAccount", return_value=mm_plex_account
|
||||
), asynctest.patch("plexauth.PlexAuth.initiate_auth"), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=MOCK_TOKEN
|
||||
):
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "external_done"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "all_configured"
|
||||
|
||||
|
@ -480,14 +544,12 @@ async def test_manual_config(hass):
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_TOKEN: "", "manual_setup": True}
|
||||
result["flow_id"], user_input={"manual_setup": True}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "manual_setup"
|
||||
|
||||
|
@ -508,13 +570,12 @@ async def test_manual_config(hass):
|
|||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_HOST: MOCK_HOST_1,
|
||||
CONF_PORT: int(MOCK_PORT_1),
|
||||
CONF_PORT: MOCK_PORT_1,
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: True,
|
||||
CONF_TOKEN: MOCK_TOKEN,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == MOCK_SERVER_1.name
|
||||
assert result["data"][config_flow.CONF_SERVER] == MOCK_SERVER_1.name
|
||||
|
@ -529,25 +590,6 @@ async def test_manual_config(hass):
|
|||
assert result["data"][config_flow.PLEX_SERVER_CONFIG][CONF_TOKEN] == MOCK_TOKEN
|
||||
|
||||
|
||||
async def test_no_token(hass):
|
||||
"""Test failing when no token provided."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"][CONF_TOKEN] == "no_token"
|
||||
|
||||
|
||||
async def test_option_flow(hass):
|
||||
"""Test config flow selection of one of two bridges."""
|
||||
|
||||
|
@ -557,7 +599,6 @@ async def test_option_flow(hass):
|
|||
result = await hass.config_entries.options.flow.async_init(
|
||||
entry.entry_id, context={"source": "test"}, data=None
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "plex_mp_settings"
|
||||
|
||||
|
@ -575,3 +616,57 @@ async def test_option_flow(hass):
|
|||
config_flow.CONF_SHOW_ALL_CONTROLS: True,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def test_external_timed_out(hass):
|
||||
"""Test when external flow times out."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with asynctest.patch("plexauth.PlexAuth.initiate_auth"), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=None
|
||||
):
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "external_done"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "token_request_timeout"
|
||||
|
||||
|
||||
async def test_callback_view(hass, aiohttp_client):
|
||||
"""Test callback view."""
|
||||
|
||||
await async_setup_component(hass, "http", {"http": {}})
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
config_flow.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with asynctest.patch("plexauth.PlexAuth.initiate_auth"), asynctest.patch(
|
||||
"plexauth.PlexAuth.token", return_value=MOCK_TOKEN
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"manual_setup": False}
|
||||
)
|
||||
assert result["type"] == "external"
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
forward_url = f'{config_flow.AUTH_CALLBACK_PATH}?flow_id={result["flow_id"]}'
|
||||
|
||||
resp = await client.get(forward_url)
|
||||
assert resp.status == 200
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue