530316c2c3
- New Z2MLightOutputTarget storage, processor, editor and routes for Zigbee2MQTT light entities (shares the HA-Light editor UI via the new light-target-editor module) - Replace global MQTTService/MQTTConfig with per-source MQTTManager + MQTTRuntime; thread mqtt_source_id through Z2M targets, DIY MQTT devices, and the automation engine - Migrate legacy single-broker YAML/env config to a "Default Broker" MQTTSource on startup (core/mqtt/legacy_migration.py) and drop the obsolete core/mqtt/mqtt_service.py - Refresh /api/v1/system integration status to surface every MQTT source - Extract shared light-target editor and refactor OutputTargetStore + output_targets routes around typed factories / auto-registry - Modal CSS polish, locale strings, and storage/bindable test coverage
77 lines
3.0 KiB
Python
77 lines
3.0 KiB
Python
"""Tests for BindableFloat / BindableColor update semantics.
|
|
|
|
The key behaviour pinned here: when ``apply_update`` receives a plain
|
|
primitive (number for float, ``[R,G,B]`` list for color) it must clear
|
|
``source_id`` -- that is how the client signals "unbind, use the static
|
|
value now". Previously the implementation preserved ``source_id`` on
|
|
plain-primitive updates, which silently dropped unbind requests.
|
|
"""
|
|
|
|
from ledgrab.storage.bindable import BindableColor, BindableFloat
|
|
|
|
|
|
class TestBindableFloatApplyUpdate:
|
|
def test_plain_number_unbinds_when_previously_bound(self):
|
|
bf = BindableFloat(value=0.5, source_id="vs_abc")
|
|
updated = bf.apply_update(0.8)
|
|
assert updated.value == 0.8
|
|
assert updated.source_id == ""
|
|
assert not updated.is_bound
|
|
|
|
def test_plain_number_leaves_unbound_unbound(self):
|
|
bf = BindableFloat(value=0.5, source_id="")
|
|
updated = bf.apply_update(0.8)
|
|
assert updated.value == 0.8
|
|
assert updated.source_id == ""
|
|
|
|
def test_dict_with_source_id_binds(self):
|
|
bf = BindableFloat(value=0.5, source_id="")
|
|
updated = bf.apply_update({"value": 0.8, "source_id": "vs_new"})
|
|
assert updated.value == 0.8
|
|
assert updated.source_id == "vs_new"
|
|
assert updated.is_bound
|
|
|
|
def test_dict_with_empty_source_id_unbinds(self):
|
|
bf = BindableFloat(value=0.5, source_id="vs_abc")
|
|
updated = bf.apply_update({"value": 0.8, "source_id": ""})
|
|
assert updated.value == 0.8
|
|
assert updated.source_id == ""
|
|
|
|
def test_none_leaves_unchanged(self):
|
|
bf = BindableFloat(value=0.5, source_id="vs_abc")
|
|
updated = bf.apply_update(None)
|
|
assert updated is bf
|
|
|
|
|
|
class TestBindableColorApplyUpdate:
|
|
def test_plain_list_unbinds_when_previously_bound(self):
|
|
bc = BindableColor(color=[10, 20, 30], source_id="vs_abc")
|
|
updated = bc.apply_update([100, 150, 200])
|
|
assert updated.color == [100, 150, 200]
|
|
assert updated.source_id == ""
|
|
assert not updated.is_bound
|
|
|
|
def test_plain_list_leaves_unbound_unbound(self):
|
|
bc = BindableColor(color=[10, 20, 30], source_id="")
|
|
updated = bc.apply_update([100, 150, 200])
|
|
assert updated.color == [100, 150, 200]
|
|
assert updated.source_id == ""
|
|
|
|
def test_dict_with_source_id_binds(self):
|
|
bc = BindableColor(color=[10, 20, 30], source_id="")
|
|
updated = bc.apply_update({"color": [100, 150, 200], "source_id": "vs_new"})
|
|
assert updated.color == [100, 150, 200]
|
|
assert updated.source_id == "vs_new"
|
|
assert updated.is_bound
|
|
|
|
def test_dict_with_empty_source_id_unbinds(self):
|
|
bc = BindableColor(color=[10, 20, 30], source_id="vs_abc")
|
|
updated = bc.apply_update({"color": [100, 150, 200], "source_id": ""})
|
|
assert updated.color == [100, 150, 200]
|
|
assert updated.source_id == ""
|
|
|
|
def test_none_leaves_unchanged(self):
|
|
bc = BindableColor(color=[10, 20, 30], source_id="vs_abc")
|
|
updated = bc.apply_update(None)
|
|
assert updated is bc
|