Refactor: real mixin, shared event helper, structural tests
FreshHarvestActions inherited from itself to graft on basket switching, which is legal Python and a trap. BasketMixin now sits above it and is inherited normally. The four control platforms each carried an identical private _fire; it moves to the base entity as fire_action. Rewriting those call sites mechanically broke two of them - select fired 'donate' instead of 'change_basket', and button lost an argument entirely, a TypeError reachable only by pressing it. Both fixed, and two tests now assert call arity and that no class inherits from itself, because neither fault is reachable from a unit test.
This commit is contained in:
@@ -91,3 +91,33 @@ def test_manifest_is_well_formed():
|
||||
def test_todo_entity_is_named(strings):
|
||||
"""todo.py declares its name via _attr_translation_key, not a description."""
|
||||
assert attr_keys("todo.py") == set(strings["entity"]["todo"])
|
||||
|
||||
|
||||
def test_fire_action_call_sites_are_well_formed():
|
||||
"""Every fire_action call passes (action, ok, target, detail).
|
||||
|
||||
A refactor that moved this helper onto the base entity rewrote the call
|
||||
sites mechanically and left one with three arguments — a TypeError that
|
||||
only fires when a user presses the button, which no unit test reaches.
|
||||
"""
|
||||
bad = []
|
||||
for path in COMPONENT.glob("*.py"):
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||
for node in ast.walk(tree):
|
||||
if (
|
||||
isinstance(node, ast.Call)
|
||||
and getattr(node.func, "attr", None) == "fire_action"
|
||||
and len(node.args) != 4
|
||||
):
|
||||
bad.append(f"{path.name}:{node.lineno} takes {len(node.args)}")
|
||||
assert not bad, f"malformed fire_action calls: {bad}"
|
||||
|
||||
|
||||
def test_no_class_inherits_from_itself():
|
||||
"""`class X(X, Mixin)` is legal Python and a maintenance trap; it was here."""
|
||||
for path in COMPONENT.glob("*.py"):
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ClassDef):
|
||||
names = {getattr(b, "id", None) for b in node.bases}
|
||||
assert node.name not in names, f"{path.name}: {node.name} inherits itself"
|
||||
|
||||
Reference in New Issue
Block a user