Rename the trigger node type to debounce, migrating stored flows
The store rewrites `"type": "trigger"` to `debounce` in every published and draft flow when it opens, bumps the version and commits once. No alias stays behind, so `trigger` is now an unknown type.
This commit is contained in:
@@ -8,8 +8,8 @@ come up than to go down.
|
||||
|
||||
from fluksio.flow.messages import DType, MessageSpec
|
||||
from fluksio.flow.nodes import MqttNode
|
||||
from fluksio.flow.nodes.debounce import DebounceNode
|
||||
from fluksio.flow.nodes.mqtt import topic_matches
|
||||
from fluksio.flow.nodes.trigger import TriggerNode
|
||||
|
||||
# ── MQTT topic filters ────────────────────────────────────────────────────
|
||||
|
||||
@@ -72,7 +72,7 @@ def test_without_json_key_the_port_name_is_the_key_as_before():
|
||||
assert node.json_keys == {}
|
||||
|
||||
|
||||
# ── trigger: passthrough and a per-message wait ───────────────────────────
|
||||
# ── debounce: passthrough and a per-message wait ──────────────────────────
|
||||
|
||||
|
||||
class _Pipeline:
|
||||
@@ -87,8 +87,8 @@ class _Pipeline:
|
||||
return True
|
||||
|
||||
|
||||
def _trigger(**params) -> tuple[TriggerNode, _Pipeline]:
|
||||
node = TriggerNode(
|
||||
def _debounce(**params) -> tuple[DebounceNode, _Pipeline]:
|
||||
node = DebounceNode(
|
||||
requires=[
|
||||
MessageSpec(name="run", port="run", dtype=DType.STR),
|
||||
MessageSpec(name="run_for", port="run_for", dtype=DType.FLOAT),
|
||||
@@ -103,30 +103,30 @@ def _trigger(**params) -> tuple[TriggerNode, _Pipeline]:
|
||||
|
||||
|
||||
def test_the_incoming_value_passes_through_and_the_wait_comes_from_a_port():
|
||||
node, pipeline = _trigger()
|
||||
node, pipeline = _debounce()
|
||||
|
||||
assert node._trigger({}, run="DOWN", run_for=28.0) == {"cmd": "DOWN"}
|
||||
assert node._debounce({}, run="DOWN", run_for=28.0) == {"cmd": "DOWN"}
|
||||
(outputs, seconds, _guard) = pipeline.deferred[-1]
|
||||
assert outputs == {"shutters.cmd": "STOP"} and seconds == 28.0
|
||||
|
||||
node._trigger({}, run="UP", run_for=26.0)
|
||||
node._debounce({}, run="UP", run_for=26.0)
|
||||
assert pipeline.deferred[-1][1] == 26.0
|
||||
|
||||
|
||||
def test_a_second_command_invalidates_the_stop_the_first_one_scheduled():
|
||||
node, pipeline = _trigger()
|
||||
node._trigger({}, run="DOWN", run_for=28.0)
|
||||
node, pipeline = _debounce()
|
||||
node._debounce({}, run="DOWN", run_for=28.0)
|
||||
first = pipeline.deferred[-1][2]
|
||||
node._trigger({}, run="UP", run_for=26.0)
|
||||
node._debounce({}, run="UP", run_for=26.0)
|
||||
assert pipeline.deferred[-1][2] != first
|
||||
|
||||
|
||||
def test_a_wait_of_zero_sends_nothing_afterwards():
|
||||
"""STOP is commanded once. Nothing follows it, forever."""
|
||||
node, pipeline = _trigger()
|
||||
node._trigger({}, run="DOWN", run_for=28.0)
|
||||
node, pipeline = _debounce()
|
||||
node._debounce({}, run="DOWN", run_for=28.0)
|
||||
scheduled = len(pipeline.deferred)
|
||||
|
||||
assert node._trigger({}, run="STOP", run_for=0.0) == {"cmd": "STOP"}
|
||||
assert node._debounce({}, run="STOP", run_for=0.0) == {"cmd": "STOP"}
|
||||
assert len(pipeline.deferred) == scheduled
|
||||
assert node.recall("armed", 0) == 0
|
||||
|
||||
@@ -75,7 +75,7 @@ FIXTURES: dict[str, dict] = {
|
||||
],
|
||||
"provides": [MessageSpec(name="reading", dtype=DType.JSON)],
|
||||
},
|
||||
"trigger": {
|
||||
"debounce": {
|
||||
"params": {"first": True, "then": False, "wait": 120},
|
||||
"requires": [MessageSpec(name="motion", dtype=DType.BOOL)],
|
||||
"provides": [MessageSpec(name="light", dtype=DType.BOOL)],
|
||||
|
||||
@@ -131,3 +131,31 @@ def test_renaming_onto_an_existing_name_is_refused(store: FlowStore):
|
||||
store.rename_flow("heating", "warmth")
|
||||
|
||||
assert store.list_flows() == ["heating", "warmth"]
|
||||
|
||||
|
||||
def test_opening_the_store_renames_trigger_nodes_to_debounce(store: FlowStore):
|
||||
"""The old name has no alias, so what is stored under it is rewritten once."""
|
||||
store.write_flow(
|
||||
FlowDef(
|
||||
name="hall",
|
||||
nodes=[NodeDef(id="hold", type="trigger"), NodeDef(id="lamp")],
|
||||
)
|
||||
)
|
||||
store.write_draft(
|
||||
FlowDef(name="hall", nodes=[NodeDef(id="hold", type="trigger")]),
|
||||
base_version=1,
|
||||
)
|
||||
commits = commit_count(store)
|
||||
|
||||
reopened = FlowStore(store.root)
|
||||
|
||||
published = reopened.read_flow("hall")
|
||||
draft = reopened.read_flow("hall", draft=True)
|
||||
assert [node.type for node in published.nodes] == ["debounce", "python"]
|
||||
assert [node.type for node in draft.nodes] == ["debounce"]
|
||||
# Bumped, so an editor holding the old draft cannot save `trigger` back.
|
||||
assert (published.version, draft.version) == (2, 3)
|
||||
assert commit_count(store) == commits + 1
|
||||
|
||||
FlowStore(store.root)
|
||||
assert commit_count(store) == commits + 1
|
||||
|
||||
Reference in New Issue
Block a user