Files
core/tests/pylint/test_now.py

271 lines
7.0 KiB
Python

"""Tests for the home-assistant-enforce-now checker."""
import astroid
from pylint.checkers import BaseChecker
from pylint.testutils.unittest_linter import UnittestLinter
import pytest
from . import assert_no_messages, walk_checker
@pytest.mark.parametrize(
"code",
[
pytest.param(
"""
from homeassistant.util import dt as dt_util
now = dt_util.now()
""",
id="now_helper",
),
pytest.param(
# Calling ``datetime.now()`` with no argument returns naive local
# time, which is not equivalent to ``dt_util.now()``.
"""
from datetime import datetime
now = datetime.now()
""",
id="now_no_args",
),
pytest.param(
# The UTC case is handled by the ``enforce-utcnow`` checker.
"""
from datetime import datetime, UTC
now = datetime.now(UTC)
""",
id="now_with_utc",
),
pytest.param(
"""
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
""",
id="now_with_timezone_utc",
),
pytest.param(
"""
import datetime
now = datetime.datetime.now(datetime.UTC)
""",
id="qualified_now_with_utc",
),
pytest.param(
"""
from datetime import datetime
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo("UTC"))
""",
id="now_with_zoneinfo_utc",
),
pytest.param(
"""
from datetime import datetime, UTC
now = datetime.now(tz=UTC)
""",
id="kwarg_now_with_utc",
),
pytest.param(
# ``UTC`` re-exported from ``homeassistant.util.dt`` is still the
# UTC case, handled by the ``enforce-utcnow`` checker.
"""
import datetime
from homeassistant.util import dt as dt_util
now = datetime.datetime.now(dt_util.UTC)
""",
id="dt_util_utc",
),
pytest.param(
"""
import datetime
from homeassistant.util import dt as dt_util
now = datetime.datetime.now(tz=dt_util.UTC)
""",
id="kwarg_dt_util_utc",
),
pytest.param(
"""
from datetime import datetime
from homeassistant.util.dt import UTC
now = datetime.now(UTC)
""",
id="from_util_dt_import_utc",
),
pytest.param(
"""
import datetime
import homeassistant.util.dt as dt_util
now = datetime.datetime.now(dt_util.UTC)
""",
id="import_util_dt_as_dt_util_utc",
),
pytest.param(
# Calling ``.now`` on something that is not ``datetime.datetime``
# must not be flagged.
"""
from zoneinfo import ZoneInfo
class Counter:
def now(self, tz):
return 0
Counter().now(ZoneInfo("Europe/Stockholm"))
""",
id="other_now_method",
),
],
)
def test_enforce_now_good(
linter: UnittestLinter,
enforce_now_checker: BaseChecker,
code: str,
) -> None:
"""Good test cases -- no message expected."""
root_node = astroid.parse(code, "homeassistant.components.pylint_test")
with assert_no_messages(linter):
walk_checker(linter, enforce_now_checker, root_node)
@pytest.mark.parametrize(
"code",
[
pytest.param(
"""
from datetime import datetime
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo("Europe/Stockholm"))
""",
id="from_import_zoneinfo",
),
pytest.param(
"""
from datetime import datetime
from zoneinfo import ZoneInfo
now = datetime.now(tz=ZoneInfo("Europe/Stockholm"))
""",
id="kwarg_zoneinfo",
),
pytest.param(
"""
import datetime
import zoneinfo
now = datetime.datetime.now(zoneinfo.ZoneInfo("Europe/Stockholm"))
""",
id="qualified_datetime",
),
pytest.param(
"""
import datetime as dt
from zoneinfo import ZoneInfo
now = dt.datetime.now(ZoneInfo("Europe/Stockholm"))
""",
id="aliased_datetime",
),
pytest.param(
# A time zone passed in as a variable is still flagged.
"""
from datetime import datetime, tzinfo
def get_now(time_zone: tzinfo) -> datetime:
return datetime.now(time_zone)
""",
id="variable_tz",
),
pytest.param(
"""
from datetime import datetime, tzinfo
def get_now(time_zone: tzinfo) -> datetime:
return datetime.now(tz=time_zone)
""",
id="kwarg_variable_tz",
),
pytest.param(
# The ``datetime`` class reached through ``homeassistant.util.dt``
# (which does ``import datetime as dt``) is still the stdlib class.
"""
from homeassistant.util import dt as dt_util
from zoneinfo import ZoneInfo
now = dt_util.dt.datetime.now(ZoneInfo("Europe/Stockholm"))
""",
id="util_dt_datetime_class",
),
pytest.param(
"""
import homeassistant.util.dt as dt_util
from zoneinfo import ZoneInfo
now = dt_util.dt.datetime.now(ZoneInfo("Europe/Stockholm"))
""",
id="import_util_dt_datetime_class",
),
],
)
def test_enforce_now_bad(
linter: UnittestLinter,
enforce_now_checker: BaseChecker,
code: str,
) -> None:
"""Bad test cases -- one message expected per call."""
root_node = astroid.parse(code, "homeassistant.components.pylint_test")
walk_checker(linter, enforce_now_checker, root_node)
messages = linter.release_messages()
assert len(messages) == 1
assert messages[0].msg_id == "home-assistant-enforce-now"
@pytest.mark.parametrize(
"code",
[
pytest.param(
"""
from datetime import datetime
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo("Europe/Stockholm"))
""",
id="from_import_datetime",
),
pytest.param(
# The form actually used in ``homeassistant/util/dt.py``.
"""
import datetime as dt
def now(time_zone: dt.tzinfo | None = None) -> dt.datetime:
return dt.datetime.now(time_zone or DEFAULT_TIME_ZONE)
""",
id="util_dt_source_form",
),
],
)
def test_enforce_now_skips_util_dt(
linter: UnittestLinter,
enforce_now_checker: BaseChecker,
code: str,
) -> None:
"""``homeassistant.util.dt`` defines ``now`` itself, so it is skipped."""
root_node = astroid.parse(code, "homeassistant.util.dt")
with assert_no_messages(linter):
walk_checker(linter, enforce_now_checker, root_node)