Do not treat nexia http not found as invalid auth (#39484)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
pull/39654/head
J. Nick Koston 2020-09-04 09:54:27 -05:00 committed by GitHub
parent 55040cfde5
commit ebc31c0f08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 98 additions and 21 deletions

View File

@ -9,18 +9,14 @@ from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
HTTP_BAD_REQUEST,
HTTP_INTERNAL_SERVER_ERROR,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, NEXIA_DEVICE, PLATFORMS, UPDATE_COORDINATOR
from .util import is_invalid_auth_code
_LOGGER = logging.getLogger(__name__)
@ -81,10 +77,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
_LOGGER.error("Unable to connect to Nexia service: %s", ex)
raise ConfigEntryNotReady from ex
except HTTPError as http_ex:
if (
http_ex.response.status_code >= HTTP_BAD_REQUEST
and http_ex.response.status_code < HTTP_INTERNAL_SERVER_ERROR
):
if is_invalid_auth_code(http_ex.response.status_code):
_LOGGER.error(
"Access error from Nexia service, please check credentials: %s", http_ex
)

View File

@ -6,14 +6,10 @@ from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
HTTP_BAD_REQUEST,
HTTP_INTERNAL_SERVER_ERROR,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from .const import DOMAIN # pylint:disable=unused-import
from .util import is_invalid_auth_code
_LOGGER = logging.getLogger(__name__)
@ -42,10 +38,7 @@ async def validate_input(hass: core.HomeAssistant, data):
raise CannotConnect from ex
except HTTPError as http_ex:
_LOGGER.error("HTTP error from Nexia service: %s", http_ex)
if (
http_ex.response.status_code >= HTTP_BAD_REQUEST
and http_ex.response.status_code < HTTP_INTERNAL_SERVER_ERROR
):
if is_invalid_auth_code(http_ex.response.status_code):
raise InvalidAuth from http_ex
raise CannotConnect from http_ex

View File

@ -1,5 +1,15 @@
"""Utils for Nexia / Trane XL Thermostats."""
from homeassistant.const import HTTP_FORBIDDEN, HTTP_UNAUTHORIZED
def is_invalid_auth_code(http_status_code):
"""HTTP status codes that mean invalid auth."""
if http_status_code in (HTTP_UNAUTHORIZED, HTTP_FORBIDDEN):
return True
return False
def percent_conv(val):
"""Convert an actual percentage (0.0-1.0) to 0-100 scale."""

View File

@ -1,5 +1,5 @@
"""Test the nexia config flow."""
from requests.exceptions import ConnectTimeout
from requests.exceptions import ConnectTimeout, HTTPError
from homeassistant import config_entries, setup
from homeassistant.components.nexia.const import DOMAIN
@ -80,6 +80,67 @@ async def test_form_cannot_connect(hass):
assert result2["errors"] == {"base": "cannot_connect"}
async def test_form_invalid_auth_http_401(hass):
"""Test we handle invalid auth error from http 401."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
response_mock = MagicMock()
type(response_mock).status_code = 401
with patch(
"homeassistant.components.nexia.config_flow.NexiaHome.login",
side_effect=HTTPError(response=response_mock),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_USERNAME: "username", CONF_PASSWORD: "password"},
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "invalid_auth"}
async def test_form_cannot_connect_not_found(hass):
"""Test we handle cannot connect from an http not found error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
response_mock = MagicMock()
type(response_mock).status_code = 404
with patch(
"homeassistant.components.nexia.config_flow.NexiaHome.login",
side_effect=HTTPError(response=response_mock),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_USERNAME: "username", CONF_PASSWORD: "password"},
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "cannot_connect"}
async def test_form_broad_exception(hass):
"""Test we handle invalid auth error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.nexia.config_flow.NexiaHome.login",
side_effect=ValueError,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_USERNAME: "username", CONF_PASSWORD: "password"},
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "unknown"}
async def test_form_import(hass):
"""Test we get the form with import source."""
await setup.async_setup_component(hass, "persistent_notification", {})

View File

@ -0,0 +1,20 @@
"""The sensor tests for the nexia platform."""
from homeassistant.components.nexia import util
from homeassistant.const import HTTP_FORBIDDEN, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED
async def test_is_invalid_auth_code():
"""Test for invalid auth."""
assert util.is_invalid_auth_code(HTTP_UNAUTHORIZED) is True
assert util.is_invalid_auth_code(HTTP_FORBIDDEN) is True
assert util.is_invalid_auth_code(HTTP_NOT_FOUND) is False
async def test_percent_conv():
"""Test percentage conversion."""
assert util.percent_conv(0.12) == 12.0
assert util.percent_conv(0.123) == 12.3