mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 08:07:45 +00:00
Add reauth and reconfigure to paperless (#145469)
* Add reauth and reconfigure * Reauth and reconfigure in different functions * Add duplicate check * Add test for reconfigure duplicate * Removed seconds config entry fixture
This commit is contained in:
parent
d0bc71752b
commit
8c971904ca
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pypaperless import Paperless
|
from pypaperless import Paperless
|
||||||
@ -36,6 +37,7 @@ class PaperlessConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
|
|
||||||
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._async_abort_entries_match(
|
self._async_abort_entries_match(
|
||||||
{
|
{
|
||||||
@ -44,31 +46,9 @@ class PaperlessConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
errors: dict[str, str] = {}
|
errors = await self._validate_input(user_input)
|
||||||
if user_input is not None:
|
|
||||||
client = Paperless(
|
|
||||||
user_input[CONF_URL],
|
|
||||||
user_input[CONF_API_KEY],
|
|
||||||
session=async_get_clientsession(self.hass),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
if not errors:
|
||||||
await client.initialize()
|
|
||||||
await client.statistics()
|
|
||||||
except PaperlessConnectionError:
|
|
||||||
errors[CONF_URL] = "cannot_connect"
|
|
||||||
except PaperlessInvalidTokenError:
|
|
||||||
errors[CONF_API_KEY] = "invalid_api_key"
|
|
||||||
except PaperlessInactiveOrDeletedError:
|
|
||||||
errors[CONF_API_KEY] = "user_inactive_or_deleted"
|
|
||||||
except PaperlessForbiddenError:
|
|
||||||
errors[CONF_API_KEY] = "forbidden"
|
|
||||||
except InitializationError:
|
|
||||||
errors[CONF_URL] = "cannot_connect"
|
|
||||||
except Exception as err: # noqa: BLE001
|
|
||||||
LOGGER.exception("Unexpected exception: %s", err)
|
|
||||||
errors["base"] = "unknown"
|
|
||||||
else:
|
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=user_input[CONF_URL], data=user_input
|
title=user_input[CONF_URL], data=user_input
|
||||||
)
|
)
|
||||||
@ -76,3 +56,96 @@ class PaperlessConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_reconfigure(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Reconfigure flow for Paperless-ngx integration."""
|
||||||
|
|
||||||
|
entry = self._get_reconfigure_entry()
|
||||||
|
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
if user_input is not None:
|
||||||
|
self._async_abort_entries_match(
|
||||||
|
{
|
||||||
|
CONF_URL: user_input[CONF_URL],
|
||||||
|
CONF_API_KEY: user_input[CONF_API_KEY],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
errors = await self._validate_input(user_input)
|
||||||
|
|
||||||
|
if not errors:
|
||||||
|
return self.async_update_reload_and_abort(entry, data=user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reconfigure",
|
||||||
|
data_schema=self.add_suggested_values_to_schema(
|
||||||
|
data_schema=STEP_USER_DATA_SCHEMA,
|
||||||
|
suggested_values={
|
||||||
|
CONF_URL: user_input[CONF_URL]
|
||||||
|
if user_input is not None
|
||||||
|
else entry.data[CONF_URL],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_reauth(
|
||||||
|
self, user_input: Mapping[str, Any]
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle re-auth."""
|
||||||
|
return await self.async_step_reauth_confirm()
|
||||||
|
|
||||||
|
async def async_step_reauth_confirm(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Reauth flow for Paperless-ngx integration."""
|
||||||
|
|
||||||
|
entry = self._get_reauth_entry()
|
||||||
|
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
if user_input is not None:
|
||||||
|
updated_data = {**entry.data, CONF_API_KEY: user_input[CONF_API_KEY]}
|
||||||
|
|
||||||
|
errors = await self._validate_input(updated_data)
|
||||||
|
|
||||||
|
if not errors:
|
||||||
|
return self.async_update_reload_and_abort(
|
||||||
|
entry,
|
||||||
|
data=updated_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reauth_confirm",
|
||||||
|
data_schema=vol.Schema({vol.Required(CONF_API_KEY): str}),
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _validate_input(self, user_input: dict[str, str]) -> dict[str, str]:
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
|
||||||
|
client = Paperless(
|
||||||
|
user_input[CONF_URL],
|
||||||
|
user_input[CONF_API_KEY],
|
||||||
|
session=async_get_clientsession(self.hass),
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await client.initialize()
|
||||||
|
await client.statistics() # test permissions on api
|
||||||
|
except PaperlessConnectionError:
|
||||||
|
errors[CONF_URL] = "cannot_connect"
|
||||||
|
except PaperlessInvalidTokenError:
|
||||||
|
errors[CONF_API_KEY] = "invalid_api_key"
|
||||||
|
except PaperlessInactiveOrDeletedError:
|
||||||
|
errors[CONF_API_KEY] = "user_inactive_or_deleted"
|
||||||
|
except PaperlessForbiddenError:
|
||||||
|
errors[CONF_API_KEY] = "forbidden"
|
||||||
|
except InitializationError:
|
||||||
|
errors[CONF_URL] = "cannot_connect"
|
||||||
|
except Exception as err: # noqa: BLE001
|
||||||
|
LOGGER.exception("Unexpected exception: %s", err)
|
||||||
|
errors["base"] = "unknown"
|
||||||
|
|
||||||
|
return errors
|
||||||
|
@ -17,7 +17,11 @@ from pypaperless.models import Statistic
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_URL
|
from homeassistant.const import CONF_API_KEY, CONF_URL
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
|
from homeassistant.exceptions import (
|
||||||
|
ConfigEntryAuthFailed,
|
||||||
|
ConfigEntryError,
|
||||||
|
ConfigEntryNotReady,
|
||||||
|
)
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
@ -63,12 +67,12 @@ class PaperlessCoordinator(DataUpdateCoordinator[Statistic]):
|
|||||||
translation_key="cannot_connect",
|
translation_key="cannot_connect",
|
||||||
) from err
|
) from err
|
||||||
except PaperlessInvalidTokenError as err:
|
except PaperlessInvalidTokenError as err:
|
||||||
raise ConfigEntryError(
|
raise ConfigEntryAuthFailed(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=DOMAIN,
|
||||||
translation_key="invalid_api_key",
|
translation_key="invalid_api_key",
|
||||||
) from err
|
) from err
|
||||||
except PaperlessInactiveOrDeletedError as err:
|
except PaperlessInactiveOrDeletedError as err:
|
||||||
raise ConfigEntryError(
|
raise ConfigEntryAuthFailed(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=DOMAIN,
|
||||||
translation_key="user_inactive_or_deleted",
|
translation_key="user_inactive_or_deleted",
|
||||||
) from err
|
) from err
|
||||||
@ -98,12 +102,12 @@ class PaperlessCoordinator(DataUpdateCoordinator[Statistic]):
|
|||||||
translation_key="forbidden",
|
translation_key="forbidden",
|
||||||
) from err
|
) from err
|
||||||
except PaperlessInvalidTokenError as err:
|
except PaperlessInvalidTokenError as err:
|
||||||
raise ConfigEntryError(
|
raise ConfigEntryAuthFailed(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=DOMAIN,
|
||||||
translation_key="invalid_api_key",
|
translation_key="invalid_api_key",
|
||||||
) from err
|
) from err
|
||||||
except PaperlessInactiveOrDeletedError as err:
|
except PaperlessInactiveOrDeletedError as err:
|
||||||
raise ConfigEntryError(
|
raise ConfigEntryAuthFailed(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=DOMAIN,
|
||||||
translation_key="user_inactive_or_deleted",
|
translation_key="user_inactive_or_deleted",
|
||||||
) from err
|
) from err
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"integration_type": "service",
|
"integration_type": "service",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["pypaperless"],
|
"loggers": ["pypaperless"],
|
||||||
"quality_scale": "bronze",
|
"quality_scale": "silver",
|
||||||
"requirements": ["pypaperless==4.1.0"]
|
"requirements": ["pypaperless==4.1.0"]
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ rules:
|
|||||||
integration-owner: done
|
integration-owner: done
|
||||||
log-when-unavailable: done
|
log-when-unavailable: done
|
||||||
parallel-updates: done
|
parallel-updates: done
|
||||||
reauthentication-flow: todo
|
reauthentication-flow: done
|
||||||
test-coverage: done
|
test-coverage: done
|
||||||
|
|
||||||
# Gold
|
# Gold
|
||||||
@ -66,7 +66,7 @@ rules:
|
|||||||
entity-translations: done
|
entity-translations: done
|
||||||
exception-translations: done
|
exception-translations: done
|
||||||
icon-translations: done
|
icon-translations: done
|
||||||
reconfiguration-flow: todo
|
reconfiguration-flow: done
|
||||||
repair-issues: todo
|
repair-issues: todo
|
||||||
stale-devices:
|
stale-devices:
|
||||||
status: exempt
|
status: exempt
|
||||||
|
@ -11,6 +11,26 @@
|
|||||||
"api_key": "API key to connect to the Paperless-ngx API"
|
"api_key": "API key to connect to the Paperless-ngx API"
|
||||||
},
|
},
|
||||||
"title": "Add Paperless-ngx instance"
|
"title": "Add Paperless-ngx instance"
|
||||||
|
},
|
||||||
|
"reauth_confirm": {
|
||||||
|
"data": {
|
||||||
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
|
},
|
||||||
|
"data_description": {
|
||||||
|
"api_key": "[%key:component::paperless_ngx::config::step::user::data_description::api_key%]"
|
||||||
|
},
|
||||||
|
"title": "Re-auth Paperless-ngx instance"
|
||||||
|
},
|
||||||
|
"reconfigure": {
|
||||||
|
"data": {
|
||||||
|
"url": "[%key:common::config_flow::data::url%]",
|
||||||
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
|
},
|
||||||
|
"data_description": {
|
||||||
|
"url": "[%key:component::paperless_ngx::config::step::user::data_description::url%]",
|
||||||
|
"api_key": "[%key:component::paperless_ngx::config::step::user::data_description::api_key%]"
|
||||||
|
},
|
||||||
|
"title": "Reconfigure Paperless-ngx instance"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@ -21,7 +41,9 @@
|
|||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
|
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
|
@ -11,7 +11,7 @@ from homeassistant.components.paperless_ngx.const import DOMAIN
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import setup_integration
|
from . import setup_integration
|
||||||
from .const import USER_INPUT
|
from .const import USER_INPUT_ONE
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
from tests.common import MockConfigEntry, load_fixture
|
||||||
|
|
||||||
@ -59,10 +59,10 @@ def mock_paperless(mock_statistic_data: MagicMock) -> Generator[AsyncMock]:
|
|||||||
def mock_config_entry() -> MockConfigEntry:
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
"""Return the default mocked config entry."""
|
"""Return the default mocked config entry."""
|
||||||
return MockConfigEntry(
|
return MockConfigEntry(
|
||||||
entry_id="paperless_ngx_test",
|
entry_id="0KLG00V55WEVTJ0CJHM0GADNGH",
|
||||||
title="Paperless-ngx",
|
title="Paperless-ngx",
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data=USER_INPUT,
|
data=USER_INPUT_ONE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,14 @@
|
|||||||
|
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_URL
|
from homeassistant.const import CONF_API_KEY, CONF_URL
|
||||||
|
|
||||||
USER_INPUT = {
|
USER_INPUT_ONE = {
|
||||||
CONF_URL: "https://192.168.69.16:8000",
|
CONF_URL: "https://192.168.69.16:8000",
|
||||||
CONF_API_KEY: "test_token",
|
CONF_API_KEY: "12345678",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
USER_INPUT_TWO = {
|
||||||
|
CONF_URL: "https://paperless.example.de",
|
||||||
|
CONF_API_KEY: "87654321",
|
||||||
|
}
|
||||||
|
|
||||||
|
USER_INPUT_REAUTH = {CONF_API_KEY: "192837465"}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'correspondent_count',
|
'translation_key': 'correspondent_count',
|
||||||
'unique_id': 'paperless_ngx_test_correspondent_count',
|
'unique_id': '0KLG00V55WEVTJ0CJHM0GADNGH_correspondent_count',
|
||||||
'unit_of_measurement': 'correspondents',
|
'unit_of_measurement': 'correspondents',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -82,7 +82,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'document_type_count',
|
'translation_key': 'document_type_count',
|
||||||
'unique_id': 'paperless_ngx_test_document_type_count',
|
'unique_id': '0KLG00V55WEVTJ0CJHM0GADNGH_document_type_count',
|
||||||
'unit_of_measurement': 'document types',
|
'unit_of_measurement': 'document types',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -133,7 +133,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'documents_inbox',
|
'translation_key': 'documents_inbox',
|
||||||
'unique_id': 'paperless_ngx_test_documents_inbox',
|
'unique_id': '0KLG00V55WEVTJ0CJHM0GADNGH_documents_inbox',
|
||||||
'unit_of_measurement': 'documents',
|
'unit_of_measurement': 'documents',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -184,7 +184,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'tag_count',
|
'translation_key': 'tag_count',
|
||||||
'unique_id': 'paperless_ngx_test_tag_count',
|
'unique_id': '0KLG00V55WEVTJ0CJHM0GADNGH_tag_count',
|
||||||
'unit_of_measurement': 'tags',
|
'unit_of_measurement': 'tags',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -235,7 +235,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'characters_count',
|
'translation_key': 'characters_count',
|
||||||
'unique_id': 'paperless_ngx_test_characters_count',
|
'unique_id': '0KLG00V55WEVTJ0CJHM0GADNGH_characters_count',
|
||||||
'unit_of_measurement': 'characters',
|
'unit_of_measurement': 'characters',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -286,7 +286,7 @@
|
|||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'documents_total',
|
'translation_key': 'documents_total',
|
||||||
'unique_id': 'paperless_ngx_test_documents_total',
|
'unique_id': '0KLG00V55WEVTJ0CJHM0GADNGH_documents_total',
|
||||||
'unit_of_measurement': 'documents',
|
'unit_of_measurement': 'documents',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
@ -19,7 +19,7 @@ from homeassistant.const import CONF_API_KEY, CONF_URL
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
from .const import USER_INPUT
|
from .const import USER_INPUT_ONE, USER_INPUT_REAUTH, USER_INPUT_TWO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, patch
|
from tests.common import MockConfigEntry, patch
|
||||||
|
|
||||||
@ -46,13 +46,58 @@ async def test_full_config_flow(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
USER_INPUT,
|
USER_INPUT_ONE,
|
||||||
)
|
)
|
||||||
|
|
||||||
config_entry = result["result"]
|
config_entry = result["result"]
|
||||||
assert config_entry.title == USER_INPUT[CONF_URL]
|
assert config_entry.title == USER_INPUT_ONE[CONF_URL]
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert config_entry.data == USER_INPUT
|
assert config_entry.data == USER_INPUT_ONE
|
||||||
|
|
||||||
|
|
||||||
|
async def test_full_reauth_flow(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test reauth an integration and finishing flow works."""
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
reauth_flow = await mock_config_entry.start_reauth_flow(hass)
|
||||||
|
assert reauth_flow["type"] is FlowResultType.FORM
|
||||||
|
assert reauth_flow["step_id"] == "reauth_confirm"
|
||||||
|
|
||||||
|
result_configure = await hass.config_entries.flow.async_configure(
|
||||||
|
reauth_flow["flow_id"], USER_INPUT_REAUTH
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result_configure["type"] is FlowResultType.ABORT
|
||||||
|
assert result_configure["reason"] == "reauth_successful"
|
||||||
|
assert mock_config_entry.data[CONF_API_KEY] == USER_INPUT_REAUTH[CONF_API_KEY]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_full_reconfigure_flow(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test reconfigure an integration and finishing flow works."""
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
reconfigure_flow = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
assert reconfigure_flow["type"] is FlowResultType.FORM
|
||||||
|
assert reconfigure_flow["step_id"] == "reconfigure"
|
||||||
|
|
||||||
|
result_configure = await hass.config_entries.flow.async_configure(
|
||||||
|
reconfigure_flow["flow_id"],
|
||||||
|
USER_INPUT_TWO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result_configure["type"] is FlowResultType.ABORT
|
||||||
|
assert result_configure["reason"] == "reconfigure_successful"
|
||||||
|
assert mock_config_entry.data == USER_INPUT_TWO
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -78,7 +123,7 @@ async def test_config_flow_error_handling(
|
|||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_USER},
|
context={"source": SOURCE_USER},
|
||||||
data=USER_INPUT,
|
data=USER_INPUT_ONE,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
@ -89,12 +134,87 @@ async def test_config_flow_error_handling(
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input=USER_INPUT,
|
user_input=USER_INPUT_ONE,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result["title"] == USER_INPUT[CONF_URL]
|
assert result["title"] == USER_INPUT_ONE[CONF_URL]
|
||||||
assert result["data"] == USER_INPUT
|
assert result["data"] == USER_INPUT_ONE
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("side_effect", "expected_error"),
|
||||||
|
[
|
||||||
|
(PaperlessConnectionError(), {CONF_URL: "cannot_connect"}),
|
||||||
|
(PaperlessInvalidTokenError(), {CONF_API_KEY: "invalid_api_key"}),
|
||||||
|
(PaperlessInactiveOrDeletedError(), {CONF_API_KEY: "user_inactive_or_deleted"}),
|
||||||
|
(PaperlessForbiddenError(), {CONF_API_KEY: "forbidden"}),
|
||||||
|
(InitializationError(), {CONF_URL: "cannot_connect"}),
|
||||||
|
(Exception("BOOM!"), {"base": "unknown"}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_reauth_flow_error_handling(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_paperless: AsyncMock,
|
||||||
|
side_effect: Exception,
|
||||||
|
expected_error: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test reauth flow with various initialization errors."""
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
mock_paperless.initialize.side_effect = side_effect
|
||||||
|
|
||||||
|
reauth_flow = await mock_config_entry.start_reauth_flow(hass)
|
||||||
|
assert reauth_flow["type"] is FlowResultType.FORM
|
||||||
|
assert reauth_flow["step_id"] == "reauth_confirm"
|
||||||
|
|
||||||
|
result_configure = await hass.config_entries.flow.async_configure(
|
||||||
|
reauth_flow["flow_id"], USER_INPUT_REAUTH
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result_configure["type"] is FlowResultType.FORM
|
||||||
|
assert result_configure["errors"] == expected_error
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("side_effect", "expected_error"),
|
||||||
|
[
|
||||||
|
(PaperlessConnectionError(), {CONF_URL: "cannot_connect"}),
|
||||||
|
(PaperlessInvalidTokenError(), {CONF_API_KEY: "invalid_api_key"}),
|
||||||
|
(PaperlessInactiveOrDeletedError(), {CONF_API_KEY: "user_inactive_or_deleted"}),
|
||||||
|
(PaperlessForbiddenError(), {CONF_API_KEY: "forbidden"}),
|
||||||
|
(InitializationError(), {CONF_URL: "cannot_connect"}),
|
||||||
|
(Exception("BOOM!"), {"base": "unknown"}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_reconfigure_flow_error_handling(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_paperless: AsyncMock,
|
||||||
|
side_effect: Exception,
|
||||||
|
expected_error: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test reconfigure flow with various initialization errors."""
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
mock_paperless.initialize.side_effect = side_effect
|
||||||
|
|
||||||
|
reauth_flow = await mock_config_entry.start_reconfigure_flow(hass)
|
||||||
|
assert reauth_flow["type"] is FlowResultType.FORM
|
||||||
|
assert reauth_flow["step_id"] == "reconfigure"
|
||||||
|
|
||||||
|
result_configure = await hass.config_entries.flow.async_configure(
|
||||||
|
reauth_flow["flow_id"],
|
||||||
|
USER_INPUT_TWO,
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result_configure["type"] is FlowResultType.FORM
|
||||||
|
assert result_configure["errors"] == expected_error
|
||||||
|
|
||||||
|
|
||||||
async def test_config_already_exists(
|
async def test_config_already_exists(
|
||||||
@ -105,8 +225,36 @@ async def test_config_already_exists(
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
data=USER_INPUT,
|
data=USER_INPUT_ONE,
|
||||||
context={"source": config_entries.SOURCE_USER},
|
context={"source": config_entries.SOURCE_USER},
|
||||||
)
|
)
|
||||||
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_config_already_exists_reconfigure(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test we only allow a single config if reconfiguring an entry."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
mock_config_entry_two = MockConfigEntry(
|
||||||
|
entry_id="J87G00V55WEVTJ0CJHM0GADBH5",
|
||||||
|
title="Paperless-ngx - Two",
|
||||||
|
domain=DOMAIN,
|
||||||
|
data=USER_INPUT_TWO,
|
||||||
|
)
|
||||||
|
mock_config_entry_two.add_to_hass(hass)
|
||||||
|
|
||||||
|
reconfigure_flow = await mock_config_entry_two.start_reconfigure_flow(hass)
|
||||||
|
assert reconfigure_flow["type"] is FlowResultType.FORM
|
||||||
|
assert reconfigure_flow["step_id"] == "reconfigure"
|
||||||
|
|
||||||
|
result_configure = await hass.config_entries.flow.async_configure(
|
||||||
|
reconfigure_flow["flow_id"],
|
||||||
|
USER_INPUT_ONE,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result_configure["type"] is FlowResultType.ABORT
|
||||||
|
assert result_configure["reason"] == "already_configured"
|
||||||
|
@ -12,6 +12,7 @@ from pypaperless.exceptions import (
|
|||||||
from pypaperless.models import Statistic
|
from pypaperless.models import Statistic
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.paperless_ngx.coordinator import UPDATE_INTERVAL
|
||||||
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
@ -60,7 +61,7 @@ async def test_statistic_sensor_state(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
freezer.tick(timedelta(seconds=120))
|
freezer.tick(timedelta(seconds=UPDATE_INTERVAL))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@ -70,12 +71,12 @@ async def test_statistic_sensor_state(
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("init_integration")
|
@pytest.mark.usefixtures("init_integration")
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"error_cls",
|
("error_cls", "assert_state"),
|
||||||
[
|
[
|
||||||
PaperlessForbiddenError,
|
(PaperlessForbiddenError, "420"),
|
||||||
PaperlessConnectionError,
|
(PaperlessConnectionError, "420"),
|
||||||
PaperlessInactiveOrDeletedError,
|
(PaperlessInactiveOrDeletedError, STATE_UNAVAILABLE),
|
||||||
PaperlessInvalidTokenError,
|
(PaperlessInvalidTokenError, STATE_UNAVAILABLE),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test__statistic_sensor_state_on_error(
|
async def test__statistic_sensor_state_on_error(
|
||||||
@ -84,28 +85,29 @@ async def test__statistic_sensor_state_on_error(
|
|||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
mock_statistic_data_update,
|
mock_statistic_data_update,
|
||||||
error_cls,
|
error_cls,
|
||||||
|
assert_state,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Ensure sensor entities are added automatically."""
|
"""Ensure sensor entities are added automatically."""
|
||||||
# simulate error
|
# simulate error
|
||||||
mock_paperless.statistics.side_effect = error_cls
|
mock_paperless.statistics.side_effect = error_cls
|
||||||
|
|
||||||
freezer.tick(timedelta(seconds=120))
|
freezer.tick(timedelta(seconds=UPDATE_INTERVAL))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("sensor.paperless_ngx_total_documents")
|
state = hass.states.get("sensor.paperless_ngx_total_documents")
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
# recover from error
|
# recover from not auth errors
|
||||||
mock_paperless.statistics = AsyncMock(
|
mock_paperless.statistics = AsyncMock(
|
||||||
return_value=Statistic.create_with_data(
|
return_value=Statistic.create_with_data(
|
||||||
mock_paperless, data=mock_statistic_data_update, fetched=True
|
mock_paperless, data=mock_statistic_data_update, fetched=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
freezer.tick(timedelta(seconds=120))
|
freezer.tick(timedelta(seconds=UPDATE_INTERVAL))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("sensor.paperless_ngx_total_documents")
|
state = hass.states.get("sensor.paperless_ngx_total_documents")
|
||||||
assert state.state == "420"
|
assert state.state == assert_state
|
||||||
|
Loading…
x
Reference in New Issue
Block a user