Files
localthings/tests/test_airconditioner_cac.py
T
Marc Billow daf7e3787f Add ruff (lint + format) and ty (type checking) to the project
Adds [tool.ruff] and [tool.ty] config to pyproject.toml with a curated
ruff rule set (E, F, W, I, UP, B, C4, SIM, RUF, ASYNC, LOG, G, PIE, RET,
PERF, N), pins ruff/ty in requirements-dev.txt, reformats the whole tree
with `ruff format`, and fixes the pre-existing lint and type-check debt
those tools surfaced so both run clean.

Production-code type fixes include: HA's ConfigFlowResult vs. the
generic FlowResult in config_flow.py, narrowing BoundEntity.desc to its
platform-specific subclass (SelectDesc/NumberDesc/SensorDesc/etc.) via
cast() instead of an unchecked annotation, converting HA device_class
strings to their proper enum types, a resolve_registry callback typed
as `object` instead of `DeviceRegistry | None`, and a couple of other
narrow correctness fixes (CA key type validation, an index-out-of-bounds
false positive from an empty-tuple fallback, a bool/dict argument swap).

Test-file fixes are mechanical: narrowing SamsungEntityDescription to
the correct subclass via isinstance()/cast() before accessing
subclass-only fields, and asserting Optional write_fn/unit_fn fields
are set before calling them.
2026-08-02 23:56:38 +00:00

74 lines
2.8 KiB
Python

"""Tests for the TP1X_DA-AC-CAC-01001_0000 cassette AC (issue #191).
0.16.0's device-type simplification dropped oneUiVersion detection on the
assumption every device it typed was already reachable via a modelNum board
token -- this board was the one exception (its oneUiVersion self-reports
"7.0 Air conditioner", but 'CAC' had never been added to the board-token
table), so it silently fell back to common caps and lost its climate entity.
This dump is NOT fully covered yet -- ten hrefs remain unbound (edge
lighting, PM1 filter, a second stateful light resource, absence-clean, four
`/settings/sound/*` resources, smart-sensing-cooling, UV LED), all genuinely
new to this board generation. That's a real device-support gap, left
documented here rather than guessed at, per the 'don't guess' rule -- fixing
the routing regression was the scope of #191.
"""
from custom_components.localthings.registry.adapter import flatten
from custom_components.localthings.registry.by_type import for_device_by_model
from custom_components.localthings.registry.discovery import discover
from tests.conftest import _load_device
_STILL_UNBOUND = frozenset(
{
"/edgelighting/vs/0",
"/filter/airdustPM1filter/vs/0",
"/light/stateful/vs/0",
"/mds/absenceclean/vs/0",
"/settings/sound/mode/vs/0",
"/settings/sound/optimization/vs/0",
"/settings/sound/output/vs/0",
"/settings/sound/volume/vs/0",
"/smartsensingcooling/vs/0",
"/uvled/vs/0",
}
)
def _resources():
return _load_device("airconditioner_cac")
def _reg(resources):
info = resources["/information/vs/0"]
return for_device_by_model(
info["x.com.samsung.da.modelNum"], info["x.com.samsung.da.description"]
)
def test_resolves_to_airconditioner_registry():
assert _reg(_resources()).name == "airconditioner"
def test_documented_coverage_gap_is_exactly_this_set():
"""Locks in the current, known-incomplete coverage so a future fix to
any of these hrefs shows up as a golden-regression diff (extra keys) to
update here, rather than silently shrinking this list unnoticed."""
resources = _resources()
reg = _reg(resources)
unbound = []
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
assert set(unbound) == _STILL_UNBOUND
def test_non_legacy_board_uses_the_generic_energy_scale():
"""This board reports /wind/strength/vs/0 (not /airflow/vs/0), so
is_legacy_board() is False and it must use the plain wh_to_kwh scale,
not the /100000 correction added for the unrelated ARTIK051_KRAC-class
board in issue #193."""
resources = _resources()
reg = _reg(resources)
bound = discover(resources, reg.capabilities, reg.pattern_capabilities)
state = flatten(bound, resources)
assert state["energy_kwh"] == round(84044 / 1000.0, 2)