Files
NestQuest/tests/test_const.py
T
orchestrator-agent df4c9eb21a Add per-child all-done and present-today binary sensors
all_done is on exactly when the child owed quests today and owes none
remaining (a zero-quest day stays off so celebrations never fire on
an empty day); present_today mirrors the presence engine's answer
for the HA-local date.
2026-09-19 11:56:10 -04:00

126 lines
4.9 KiB
Python

"""Tests for const.py of NestQuest."""
from __future__ import annotations
import ast
from datetime import datetime
import logging
from pathlib import Path
from custom_components.nestquest import const
def test_domain() -> None:
"""Verify DOMAIN is defined and equals 'nestquest'."""
assert hasattr(const, "DOMAIN")
assert const.DOMAIN == "nestquest"
assert isinstance(const.DOMAIN, str)
def test_logger() -> None:
"""Verify LOGGER_NAME and LOGGER are defined with correct types and values."""
assert hasattr(const, "LOGGER_NAME")
assert const.LOGGER_NAME == "custom_components.nestquest"
assert isinstance(const.LOGGER_NAME, str)
assert hasattr(const, "LOGGER")
assert isinstance(const.LOGGER, logging.Logger)
assert const.LOGGER.name == "custom_components.nestquest"
def test_platforms() -> None:
"""Verify PLATFORMS is defined as the sensor + binary_sensor list."""
assert hasattr(const, "PLATFORMS")
assert isinstance(const.PLATFORMS, list)
assert const.PLATFORMS == ["sensor", "binary_sensor"]
def test_sqlite_db_filename() -> None:
"""Verify SQLITE_DB_FILENAME is defined and equals 'nestquest.db'."""
assert hasattr(const, "SQLITE_DB_FILENAME")
assert const.SQLITE_DB_FILENAME == "nestquest.db"
assert isinstance(const.SQLITE_DB_FILENAME, str)
def test_horizon_days_config() -> None:
"""Verify CONF_HORIZON_DAYS and DEFAULT_HORIZON_DAYS are valid."""
assert hasattr(const, "CONF_HORIZON_DAYS")
assert const.CONF_HORIZON_DAYS == "horizon_days"
assert hasattr(const, "DEFAULT_HORIZON_DAYS")
assert isinstance(const.DEFAULT_HORIZON_DAYS, int)
assert const.DEFAULT_HORIZON_DAYS == 14
assert const.DEFAULT_HORIZON_DAYS > 0
def test_day_rollover_time_config() -> None:
"""Verify CONF_DAY_ROLLOVER_TIME and DEFAULT_DAY_ROLLOVER_TIME are valid."""
assert hasattr(const, "CONF_DAY_ROLLOVER_TIME")
assert const.CONF_DAY_ROLLOVER_TIME == "day_rollover_time"
assert hasattr(const, "DEFAULT_DAY_ROLLOVER_TIME")
assert isinstance(const.DEFAULT_DAY_ROLLOVER_TIME, str)
assert const.DEFAULT_DAY_ROLLOVER_TIME == "00:00"
# Validate time format HH:MM
parsed = datetime.strptime(const.DEFAULT_DAY_ROLLOVER_TIME, "%H:%M")
assert parsed.hour == 0 and parsed.minute == 0
def test_panel_idle_timeout_config() -> None:
"""Verify CONF_PANEL_IDLE_TIMEOUT and DEFAULT_PANEL_IDLE_TIMEOUT are valid."""
assert hasattr(const, "CONF_PANEL_IDLE_TIMEOUT")
assert const.CONF_PANEL_IDLE_TIMEOUT == "panel_idle_timeout"
assert hasattr(const, "DEFAULT_PANEL_IDLE_TIMEOUT")
assert isinstance(const.DEFAULT_PANEL_IDLE_TIMEOUT, int)
assert const.DEFAULT_PANEL_IDLE_TIMEOUT == 300
assert const.DEFAULT_PANEL_IDLE_TIMEOUT > 0
def test_quest_windows_canonical_order() -> None:
"""QUEST_WINDOWS is exactly the three spellings in canonical order."""
assert const.QUEST_WINDOWS == ("morning", "afternoon", "evening")
assert const.WINDOW_MORNING == "morning"
assert const.WINDOW_AFTERNOON == "afternoon"
assert const.WINDOW_EVENING == "evening"
# No duplicates, every name covered by its individual constant.
assert len(set(const.QUEST_WINDOWS)) == 3
def test_window_clock_ranges_cover_every_window() -> None:
"""Each window has a strict HH:MM (start, end) range.
The ranges follow the documented boundaries (D-008): morning ends
11:59, afternoon spans 12:00-17:00, evening 17:00-21:00, and each
window's end equals the next window's start (the boundary belongs
to the later window at classification time).
"""
assert const.WINDOW_CLOCK_RANGES == {
"morning": ("00:00", "11:59"),
"afternoon": ("12:00", "17:00"),
"evening": ("17:00", "21:00"),
}
for window in const.QUEST_WINDOWS:
start, end = const.WINDOW_CLOCK_RANGES[window]
for value in (start, end):
parsed = datetime.strptime(value, "%H:%M").time()
assert parsed.strftime("%H:%M") == value
def test_no_hardcoded_domain_or_db_filename_in_modules() -> None:
"""Ensure no module other than const.py hardcodes DOMAIN string or SQLITE_DB_FILENAME."""
pkg_dir = Path(const.__file__).parent
py_files = [f for f in pkg_dir.glob("*.py") if f.name != "const.py"]
assert len(py_files) > 0, "No python modules found in package to check"
for py_file in py_files:
content = py_file.read_text(encoding="utf-8")
tree = ast.parse(content, filename=str(py_file))
for node in ast.walk(tree):
if isinstance(node, ast.Constant) and isinstance(node.value, str):
assert node.value != "nestquest.db", (
f"Found hard-coded database filename '{node.value}' in {py_file.name}:{node.lineno}"
)
if node.value == "nestquest":
raise AssertionError(
f"Found hard-coded domain string '{node.value}' in {py_file.name}:{node.lineno}"
)