Skip to content

Commit 6fb361a

Browse files
authored
Add strict validation via pydantic for all events (#361)
1 parent a6338f4 commit 6fb361a

33 files changed

+660
-62
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: Upload coverage data
3737
uses: "actions/[email protected]"
3838
with:
39-
name: coverage-data
39+
name: coverage-${{ matrix.python-version }}
4040
path: "coverage.xml"
4141

4242
coverage:
@@ -47,9 +47,7 @@ jobs:
4747
uses: actions/[email protected]
4848
with:
4949
fetch-depth: 2
50-
- name: Download coverage data
50+
- name: Download all coverage data
5151
uses: actions/[email protected]
52-
with:
53-
name: coverage-data
5452
- name: Upload coverage report
5553
uses: codecov/[email protected]

pylintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ignore=tests
44
# any too bad. Override on command line as appropriate.
55
jobs=2
66
persistent=no
7+
extension-pkg-whitelist=pydantic
78

89
[BASIC]
910
good-names=id,i,j,k,ex,Run,_,fp

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
aiohttp==3.8.1
2+
pydantic==1.9.0

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use_parentheses = True
2222
line_length = 88
2323

2424
[mypy]
25+
plugins = pydantic.mypy
2526
follow_imports = skip
2627
ignore_missing_imports = true
2728
check_untyped_defs = true

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
long_description_content_type="text/markdown",
2121
packages=find_packages(exclude=["test.*", "test"]),
2222
python_requires=">=3.8",
23-
install_requires=["aiohttp>3"],
23+
install_requires=["aiohttp>3", "pydantic>=1.9.0"],
2424
entry_points={
2525
"console_scripts": ["zwave-js-server-python = zwave_js_server.__main__:main"]
2626
},

test/fixtures/multisensor_6_state.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"generic": { "key": 2, "label": "Multilevel Sensor" },
1111
"specific": { "key": 3, "label": "Routing Multilevel Sensor" },
1212
"mandatorySupportedCCs": [],
13-
"mandatoryControlCCs": []
13+
"mandatoryControlledCCs": []
1414
},
1515
"isListening": true,
1616
"isFrequentListening": false,

test/model/test_controller.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,3 +1358,43 @@ async def test_nvm_events(controller):
13581358
)
13591359
controller.receive_event(event)
13601360
assert event.data["nvm_restore_progress"] == controller_pkg.NVMProgress(5, 6)
1361+
1362+
1363+
async def test_node_added(controller, multisensor_6_state):
1364+
"""Test node added event."""
1365+
event = Event(
1366+
"node added",
1367+
{
1368+
"source": "controller",
1369+
"event": "node added",
1370+
"node": multisensor_6_state,
1371+
"result": "",
1372+
},
1373+
)
1374+
controller.receive_event(event)
1375+
assert event.data["node"].node_id == 52
1376+
1377+
1378+
async def test_node_removed(client, multisensor_6, multisensor_6_state):
1379+
"""Test node removed event."""
1380+
event = Event(
1381+
"node removed",
1382+
{
1383+
"source": "controller",
1384+
"event": "node removed",
1385+
"node": multisensor_6_state,
1386+
"replaced": False,
1387+
},
1388+
)
1389+
client.driver.controller.receive_event(event)
1390+
assert event.data["node"].node_id == 52
1391+
assert event.data["node"].client is None
1392+
assert 52 not in client.driver.controller.nodes
1393+
1394+
1395+
async def test_unknown_event(controller):
1396+
"""Test that an unknown event type causes an exception."""
1397+
with pytest.raises(KeyError):
1398+
assert controller.receive_event(
1399+
Event("unknown_event", {"source": "controller"})
1400+
)

test/model/test_driver.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Test the driver model."""
22
import json
33

4+
import pytest
5+
46
from zwave_js_server.const import LogLevel
57
from zwave_js_server.event import Event
68
from zwave_js_server.model import driver as driver_pkg
@@ -315,3 +317,9 @@ async def test_set_preferred_scales(driver, uuid4, mock_command):
315317
"scales": {1: 1},
316318
"messageId": uuid4,
317319
}
320+
321+
322+
async def test_unknown_event(driver):
323+
"""Test that an unknown event type causes an exception."""
324+
with pytest.raises(KeyError):
325+
assert driver.receive_event(Event("unknown_event", {"source": "driver"}))

test/model/test_node.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ async def test_abort_firmware_update(multisensor_6, uuid4, mock_command):
438438
}
439439

440440

441-
def test_node_inclusion():
441+
def test_node_inclusion(multisensor_6_state):
442442
"""Emulate a node being added."""
443443
# when a node node is added, it has minimal info first
444444
node = node_pkg.Node(
@@ -451,8 +451,16 @@ def test_node_inclusion():
451451
assert node.device_config.manufacturer is None
452452

453453
# the ready event contains a full (and complete) dump of the node, including values
454-
state = json.loads(load_fixture("multisensor_6_state.json"))
455-
event = Event("ready", {"nodeState": state})
454+
event = Event(
455+
"ready",
456+
{
457+
"event": "ready",
458+
"source": "node",
459+
"nodeId": node.node_id,
460+
"nodeState": multisensor_6_state,
461+
"result": [],
462+
},
463+
)
456464
node.receive_event(event)
457465

458466
assert node.device_config.manufacturer == "AEON Labs"
@@ -1102,6 +1110,7 @@ async def test_statistics_updated(wallmote_central_scene: node_pkg.Node):
11021110
{
11031111
"source": "node",
11041112
"event": "statistics updated",
1113+
"nodeId": node.node_id,
11051114
"statistics": {
11061115
"commandsTX": 1,
11071116
"commandsRX": 1,
@@ -1441,3 +1450,9 @@ async def test_set_keep_awake(multisensor_6: node_pkg.Node, uuid4, mock_command)
14411450
"keepAwake": False,
14421451
"messageId": uuid4,
14431452
}
1453+
1454+
1455+
async def test_unknown_event(multisensor_6: node_pkg.Node):
1456+
"""Test that an unknown event type causes an exception."""
1457+
with pytest.raises(KeyError):
1458+
assert multisensor_6.receive_event(Event("unknown_event", {"source": "node"}))

test/test_event.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"""Test event helpers."""
2-
32
from zwave_js_server import event
43

54

0 commit comments

Comments
 (0)