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.
78 lines
2.9 KiB
Python
78 lines
2.9 KiB
Python
"""Tests that known-noise hrefs don't show up as coverage gaps.
|
|
|
|
Each device-type registry folds in registry.capabilities.ignored.IGNORED so
|
|
that Bixby/voice/network/OTA housekeeping resources aren't reported as
|
|
unbound. This guards two things at once: that the ignored hrefs are
|
|
actually suppressed, and (via a plain import) that no ignored href
|
|
collides with a real capability declared elsewhere for the same family.
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from custom_components.localthings.registry.batch import parse_device0_batch
|
|
from custom_components.localthings.registry.by_type import dishwasher, refrigerator
|
|
from custom_components.localthings.registry.capabilities.ignored import IGNORED
|
|
|
|
FIXTURES = Path(__file__).resolve().parent / "fixtures"
|
|
|
|
_IGNORED_HREFS = {cap.href for cap in IGNORED}
|
|
|
|
|
|
def _load(name: str) -> dict:
|
|
data = json.loads((FIXTURES / name).read_text())
|
|
return parse_device0_batch(data["device0"])
|
|
|
|
|
|
def _unbound_hrefs(resources, registry) -> list[str]:
|
|
unbound = []
|
|
from custom_components.localthings.registry.discovery import discover
|
|
|
|
discover(resources, registry.capabilities, registry.pattern_capabilities, log=unbound.append)
|
|
return unbound
|
|
|
|
|
|
def test_ignored_hrefs_absent_from_dishwasher_gaps():
|
|
resources = _load("dishwasher_device.json")
|
|
unbound = _unbound_hrefs(resources, dishwasher.REGISTRY)
|
|
present_ignored = _IGNORED_HREFS & set(unbound)
|
|
assert not present_ignored, f"ignored hrefs leaked into gap report: {present_ignored}"
|
|
|
|
|
|
def test_ignored_hrefs_absent_from_refrigerator_gaps():
|
|
resources = _load("refrigerator_device.json")
|
|
unbound = _unbound_hrefs(resources, refrigerator.REGISTRY)
|
|
present_ignored = _IGNORED_HREFS & set(unbound)
|
|
assert not present_ignored, f"ignored hrefs leaked into gap report: {present_ignored}"
|
|
|
|
|
|
def test_dishwasher_fixture_has_no_remaining_gaps():
|
|
"""Sanity check that the gap mechanism isn't silencing everything.
|
|
|
|
Every href in the dishwasher fixture is now either mapped to a
|
|
capability or listed in capabilities.ignored — if this starts failing
|
|
because a genuinely new gap appeared, that's the interesting case to
|
|
look at (add the href to ignored.py or map it, don't just delete this
|
|
assertion).
|
|
"""
|
|
resources = _load("dishwasher_device.json")
|
|
unbound = set(_unbound_hrefs(resources, dishwasher.REGISTRY))
|
|
assert unbound == set()
|
|
|
|
|
|
class TestWasherIgnoredHrefs:
|
|
def test_washer_hrefs_are_ignored(self):
|
|
from custom_components.localthings.registry.capabilities.ignored import IGNORED
|
|
|
|
ignored_hrefs = {cap.href for cap in IGNORED}
|
|
for href in (
|
|
"/st/washercourse/vs/0",
|
|
"/wm/welcomemsg/vs/0",
|
|
"/wm/personalcourse/vs/0",
|
|
"/energy/consumption/0",
|
|
"/cycleinterface/vs/0",
|
|
"/drlc/0",
|
|
"/operational/state/0",
|
|
):
|
|
assert href in ignored_hrefs, f"{href} should be in ignored.IGNORED"
|