888f8fd16e
ruff --select UP007,UP045 --fix converted ~1760 sites across the backend: `Optional[T]` → `T | None`, `Union[X, Y]` → `X | Y`. The remaining module-level alias targets that ruff conservatively skips (BindableFloatInput, ColorList, DeviceConfig) were converted by hand earlier in the pass. black -formatted the result so the wider unions fit cleanly under the 100-char line budget. pyproject.toml now sets [tool.ruff.lint] extend-select = ["UP007", "UP045"] so future legacy imports fire CI on every push. The pre-commit ruff hook was bumped from v0.8.0 -> v0.15.12 to recognise UP045 (split off from UP007 in v0.13).
69 lines
2.3 KiB
Python
69 lines
2.3 KiB
Python
"""Tests for the output-target response-builder registry.
|
|
|
|
Locks in the safety net that replaced the previous silent
|
|
``LedOutputTargetResponse(defaults)`` fallback in
|
|
``output_targets._target_to_response``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from ledgrab.api.routes import output_targets
|
|
from ledgrab.api.routes.output_targets import (
|
|
_assert_target_response_coverage,
|
|
_target_to_response,
|
|
_TARGET_RESPONSE_BUILDERS,
|
|
)
|
|
from ledgrab.storage.ha_light_output_target import HALightOutputTarget
|
|
from ledgrab.storage.wled_output_target import WledOutputTarget
|
|
from ledgrab.storage.z2m_light_output_target import Z2MLightOutputTarget
|
|
|
|
EXPECTED_TARGET_CLASSES = {WledOutputTarget, HALightOutputTarget, Z2MLightOutputTarget}
|
|
|
|
|
|
def test_registry_covers_every_known_target_subclass():
|
|
assert set(_TARGET_RESPONSE_BUILDERS.keys()) == EXPECTED_TARGET_CLASSES
|
|
|
|
|
|
def test_every_registered_builder_is_callable():
|
|
for cls, builder in _TARGET_RESPONSE_BUILDERS.items():
|
|
assert callable(builder), f"builder for {cls.__name__} is not callable"
|
|
|
|
|
|
def test_unregistered_target_class_raises_in_target_to_response():
|
|
"""Reaching ``_target_to_response`` with an unmapped class raises loudly
|
|
instead of silently returning a malformed LedOutputTargetResponse.
|
|
"""
|
|
|
|
class _RogueTarget:
|
|
id = "tgt_rogue"
|
|
name = "rogue"
|
|
|
|
with pytest.raises(RuntimeError, match="No response builder"):
|
|
_target_to_response(_RogueTarget())
|
|
|
|
|
|
def test_coverage_assertion_raises_on_drift(monkeypatch):
|
|
"""Removing a builder from the registry makes the import-time check fail."""
|
|
pruned = {
|
|
cls: builder
|
|
for cls, builder in _TARGET_RESPONSE_BUILDERS.items()
|
|
if cls is not WledOutputTarget
|
|
}
|
|
monkeypatch.setattr(output_targets, "_TARGET_RESPONSE_BUILDERS", pruned)
|
|
with pytest.raises(RuntimeError, match="WledOutputTarget"):
|
|
_assert_target_response_coverage()
|
|
|
|
|
|
def test_coverage_assertion_raises_on_extra_entry(monkeypatch):
|
|
"""An entry keyed by an unknown class is also caught."""
|
|
|
|
class _RogueTargetClass:
|
|
pass
|
|
|
|
extended = {**_TARGET_RESPONSE_BUILDERS, _RogueTargetClass: lambda t: None}
|
|
monkeypatch.setattr(output_targets, "_TARGET_RESPONSE_BUILDERS", extended)
|
|
with pytest.raises(RuntimeError, match="_RogueTargetClass"):
|
|
_assert_target_response_coverage()
|