what_to_wear/tests/test_const_schema.py
Nora eff3fa28a9 feat(1.1): Skeleton, pure-core Datenmodell, Options-Schema & Layering-Guard
Story 1.1 (TDD, Suite gruen 19/19):
- logic/model.py: frozen Dataclasses (NormField, RawForecast, NormForecast,
  Requirement, Item, ChosenItem, Gap, Outfit, Recommendation) + Enums
  RequirementKey/Category/Priority (AD-14/21/22).
- const.py: normatives flaches Options-Schema + default_options()-Deepcopy,
  TO_REDACT, Timeouts, LLM-URLs (AD-5/6/23).
- manifest.json/hacs.json (min HA 2025.3), Layering-Guard per AST (AD-1).
- luna-pro-Story-Review: 4/6 Findings uebernommen, 2 verworfen (Evidenz im Ledger).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 13:20:42 +00:00

69 lines
2.3 KiB
Python

"""Story 1.1 — the normative options schema lives once in const.py (AD-23)."""
from __future__ import annotations
from custom_components.what_to_wear import const
def test_domain() -> None:
assert const.DOMAIN == "what_to_wear"
def test_options_schema_keys_flat_english() -> None:
keys = set(const.OPTIONS_DEFAULTS)
expected = {
"warmth_band_limits",
"heat_threshold",
"rain_prob_should",
"rain_prob_must",
"rain_amount_should",
"rain_amount_must",
"gust_should",
"wind_proxy_should",
"uv_should",
"cold_sensitivity_offset",
"switchover_time",
"llm_enabled",
"llm_provider",
"llm_api_key",
"llm_model",
}
assert expected <= keys, f"missing option keys: {expected - keys}"
def test_warmth_band_limits_strictly_monotone_length_four() -> None:
limits = const.OPTIONS_DEFAULTS["warmth_band_limits"]
assert len(limits) == 4
assert all(a < b for a, b in zip(limits, limits[1:])), "must be strictly monotone"
def test_defaults_sane() -> None:
d = const.OPTIONS_DEFAULTS
assert d["cold_sensitivity_offset"] == 0
assert d["llm_enabled"] is False
assert d["switchover_time"] == "10:00:00"
assert d["heat_threshold"] == 28.0
def test_default_options_returns_independent_deep_copy() -> None:
a = const.default_options()
b = const.default_options()
a["warmth_band_limits"].append(99.0)
a["cold_sensitivity_offset"] = 2
# Mutating one copy must not affect another copy nor the template.
assert b["warmth_band_limits"] == [0.0, 8.0, 15.0, 22.0]
assert b["cold_sensitivity_offset"] == 0
assert const.OPTIONS_DEFAULTS["warmth_band_limits"] == [0.0, 8.0, 15.0, 22.0]
def test_to_redact_uses_real_key_names() -> None:
# AD-5: redaction constant must match the real stored key names.
assert const.TO_REDACT == {"llm_api_key", "weather_entity_id"}
def test_timeouts_and_urls_present() -> None:
assert const.FORECAST_TIMEOUT_S == 10
assert const.LLM_TIMEOUT_S == 18
assert const.LLM_MAX_RESPONSE_BYTES == 64 * 1024
assert const.OPENAI_URL.startswith("https://api.openai.com/")
assert const.ANTHROPIC_URL.startswith("https://api.anthropic.com/")
assert const.ANTHROPIC_VERSION == "2023-06-01"