Update country `province` validation (#84463)

* Update country `province` validation.

* Run pre-commit.

* Add tests

* Mod config flow

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
pull/98649/head
Arkadii Yakovets 2023-08-18 12:20:04 -07:00 committed by GitHub
parent f96446cb24
commit 7827f9ccae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 18 deletions

View File

@ -4,8 +4,13 @@ from __future__ import annotations
from datetime import date, timedelta
from typing import Any
import holidays
from holidays import DateLike, HolidayBase
from holidays import (
DateLike,
HolidayBase,
__version__ as python_holidays_version,
country_holidays,
list_supported_countries,
)
import voluptuous as vol
from homeassistant.components.binary_sensor import (
@ -43,7 +48,6 @@ from .const import (
def valid_country(value: Any) -> str:
"""Validate that the given country is supported."""
value = cv.string(value)
all_supported_countries = holidays.list_supported_countries()
try:
raw_value = value.encode("utf-8")
@ -53,7 +57,7 @@ def valid_country(value: Any) -> str:
) from err
if not raw_value:
raise vol.Invalid("Country name or the abbreviation must not be empty.")
if value not in all_supported_countries:
if value not in list_supported_countries():
raise vol.Invalid("Country is not supported.")
return value
@ -123,17 +127,17 @@ async def async_setup_entry(
province: str | None = entry.options.get(CONF_PROVINCE)
sensor_name: str = entry.options[CONF_NAME]
workdays: list[str] = entry.options[CONF_WORKDAYS]
cls: HolidayBase = getattr(holidays, country)
year: int = (dt_util.now() + timedelta(days=days_offset)).year
if province and province not in cls.subdivisions:
if country and country not in list_supported_countries():
LOGGER.error("There is no country %s", country)
return
if province and province not in list_supported_countries()[country]:
LOGGER.error("There is no subdivision %s in country %s", province, country)
return
obj_holidays = cls(
subdiv=province, years=year, language=cls.default_language
) # type: ignore[operator]
obj_holidays: HolidayBase = country_holidays(country, subdiv=province, years=year)
# Add custom holidays
try:
@ -209,7 +213,7 @@ class IsWorkdaySensor(BinarySensorEntity):
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, entry_id)},
manufacturer="python-holidays",
model=holidays.__version__,
model=python_holidays_version,
name=name,
)

View File

@ -3,8 +3,7 @@ from __future__ import annotations
from typing import Any
import holidays
from holidays import HolidayBase, list_supported_countries
from holidays import HolidayBase, country_holidays, list_supported_countries
import voluptuous as vol
from homeassistant.config_entries import (
@ -77,12 +76,14 @@ def validate_custom_dates(user_input: dict[str, Any]) -> None:
if dt_util.parse_date(add_date) is None:
raise AddDatesError("Incorrect date")
cls: HolidayBase = getattr(holidays, user_input[CONF_COUNTRY])
cls: HolidayBase = country_holidays(user_input[CONF_COUNTRY])
year: int = dt_util.now().year
obj_holidays = cls(
subdiv=user_input.get(CONF_PROVINCE), years=year, language=cls.default_language
) # type: ignore[operator]
obj_holidays: HolidayBase = country_holidays(
user_input[CONF_COUNTRY],
subdiv=user_input.get(CONF_PROVINCE),
years=year,
language=cls.default_language,
)
for remove_date in user_input[CONF_REMOVE_HOLIDAYS]:
if dt_util.parse_date(remove_date) is None:

View File

@ -50,6 +50,15 @@ TEST_CONFIG_WITH_PROVINCE = {
"add_holidays": [],
"remove_holidays": [],
}
TEST_CONFIG_INCORRECT_COUNTRY = {
"name": DEFAULT_NAME,
"country": "ZZ",
"excludes": DEFAULT_EXCLUDES,
"days_offset": DEFAULT_OFFSET,
"workdays": DEFAULT_WORKDAYS,
"add_holidays": [],
"remove_holidays": [],
}
TEST_CONFIG_INCORRECT_PROVINCE = {
"name": DEFAULT_NAME,
"country": "DE",

View File

@ -17,6 +17,7 @@ from . import (
TEST_CONFIG_EXAMPLE_2,
TEST_CONFIG_INCLUDE_HOLIDAY,
TEST_CONFIG_INCORRECT_ADD_REMOVE,
TEST_CONFIG_INCORRECT_COUNTRY,
TEST_CONFIG_INCORRECT_PROVINCE,
TEST_CONFIG_NO_PROVINCE,
TEST_CONFIG_NO_STATE,
@ -187,6 +188,21 @@ async def test_setup_day_after_tomorrow(
assert state.state == "off"
async def test_setup_faulty_country(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test setup with faulty province."""
freezer.move_to(datetime(2017, 1, 6, 12, tzinfo=UTC)) # Friday
await init_integration(hass, TEST_CONFIG_INCORRECT_COUNTRY)
state = hass.states.get("binary_sensor.workday_sensor")
assert state is None
assert "There is no country" in caplog.text
async def test_setup_faulty_province(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,