Skip to content

Commit bdba1a4

Browse files
committed
add scaffolding for tests
1 parent 05f18b3 commit bdba1a4

10 files changed

+5806
-1
lines changed

tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Fixtures for lock_code_manager tests."""
2+
23
import pytest
34

4-
pytest_plugins = "pytest_homeassistant_custom_component"
5+
pytest_plugins = ["pytest_homeassistant_custom_component"]
56

67

78
@pytest.fixture(autouse=True)

tests/helpers.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""Helpers for lock_code_manager tests."""
2+
3+
from functools import lru_cache
4+
from pathlib import Path
5+
import traceback
6+
7+
8+
@lru_cache
9+
def load_fixture(path_from_fixtures_folder: str) -> str:
10+
"""Load a fixture."""
11+
parent_path = Path(traceback.extract_stack()[-2].filename).parent
12+
return (parent_path / "fixtures" / path_from_fixtures_folder).read_text()

tests/zwave_js/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Z-Wave JS lock tests."""

tests/zwave_js/conftest.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
"""Fixtures for lock_code_manager tests."""
2+
3+
import asyncio
4+
import copy
5+
import json
6+
from unittest.mock import DEFAULT, AsyncMock, patch
7+
8+
import pytest
9+
from zwave_js_server.model.driver import Driver
10+
from zwave_js_server.model.node import Node
11+
from zwave_js_server.version import VersionInfo
12+
13+
from homeassistant.core import HomeAssistant
14+
15+
from pytest_homeassistant_custom_component.common import MockConfigEntry
16+
17+
from tests.helpers import load_fixture
18+
19+
20+
# Z-Wave JS fixtures
21+
22+
23+
@pytest.fixture(name="controller_state", scope="session")
24+
def controller_state_fixture():
25+
"""Load the controller state fixture data."""
26+
return json.loads(load_fixture("controller_state.json"))
27+
28+
29+
@pytest.fixture(name="controller_node_state", scope="session")
30+
def controller_node_state_fixture():
31+
"""Load the controller node state fixture data."""
32+
return json.loads(load_fixture("controller_node_state.json"))
33+
34+
35+
@pytest.fixture(name="version_state", scope="session")
36+
def version_state_fixture():
37+
"""Load the version state fixture data."""
38+
return {
39+
"type": "version",
40+
"driverVersion": "6.0.0-beta.0",
41+
"serverVersion": "1.0.0",
42+
"homeId": 1234567890,
43+
}
44+
45+
46+
@pytest.fixture(name="log_config_state")
47+
def log_config_state_fixture():
48+
"""Return log config state fixture data."""
49+
return {
50+
"enabled": True,
51+
"level": "info",
52+
"logToFile": False,
53+
"filename": "",
54+
"forceConsole": False,
55+
}
56+
57+
58+
@pytest.fixture(name="lock_schlage_be469_state", scope="session")
59+
def lock_schlage_be469_state_fixture():
60+
"""Load the schlage lock node state fixture data."""
61+
return json.loads(
62+
load_fixture("lock_schlage_be469_state.json")
63+
)
64+
65+
66+
@pytest.fixture(name="lock_august_asl03_state", scope="session")
67+
def lock_august_asl03_state_fixture():
68+
"""Load the August Pro lock node state fixture data."""
69+
return json.loads(
70+
load_fixture("lock_august_asl03_state.json")
71+
)
72+
73+
74+
@pytest.fixture(name="lock_id_lock_as_id150_state", scope="session")
75+
def lock_id_lock_as_id150_state_fixture():
76+
"""Load the id lock id-150 lock node state fixture data."""
77+
return json.loads(
78+
load_fixture("/lock_id_lock_as_id150_state.json")
79+
)
80+
81+
82+
# model fixtures
83+
84+
85+
@pytest.fixture(name="listen_block")
86+
def mock_listen_block_fixture():
87+
"""Mock a listen block."""
88+
return asyncio.Event()
89+
90+
91+
@pytest.fixture(name="client")
92+
def mock_client_fixture(
93+
controller_state,
94+
controller_node_state,
95+
version_state,
96+
log_config_state,
97+
listen_block,
98+
):
99+
"""Mock a client."""
100+
with patch(
101+
"homeassistant.components.zwave_js.ZwaveClient", autospec=True
102+
) as client_class:
103+
client = client_class.return_value
104+
105+
async def connect():
106+
await asyncio.sleep(0)
107+
client.connected = True
108+
109+
async def listen(driver_ready: asyncio.Event) -> None:
110+
driver_ready.set()
111+
await listen_block.wait()
112+
113+
async def disconnect():
114+
client.connected = False
115+
116+
client.connect = AsyncMock(side_effect=connect)
117+
client.listen = AsyncMock(side_effect=listen)
118+
client.disconnect = AsyncMock(side_effect=disconnect)
119+
client.driver = Driver(
120+
client, copy.deepcopy(controller_state), copy.deepcopy(log_config_state)
121+
)
122+
node = Node(client, copy.deepcopy(controller_node_state))
123+
client.driver.controller.nodes[node.node_id] = node
124+
125+
client.version = VersionInfo.from_message(version_state)
126+
client.ws_server_url = "ws://test:3000/zjs"
127+
128+
async def async_send_command_side_effect(message, require_schema=None):
129+
"""Return the command response."""
130+
if message["command"] == "node.has_device_config_changed":
131+
return {"changed": False}
132+
return DEFAULT
133+
134+
client.async_send_command.return_value = {
135+
"result": {"success": True, "status": 255}
136+
}
137+
client.async_send_command.side_effect = async_send_command_side_effect
138+
139+
yield client
140+
141+
142+
@pytest.fixture(name="lock_schlage_be469")
143+
def lock_schlage_be469_fixture(client, lock_schlage_be469_state):
144+
"""Mock a schlage lock node."""
145+
node = Node(client, copy.deepcopy(lock_schlage_be469_state))
146+
client.driver.controller.nodes[node.node_id] = node
147+
return node
148+
149+
150+
@pytest.fixture(name="lock_august_pro")
151+
def lock_august_asl03_fixture(client, lock_august_asl03_state):
152+
"""Mock a August Pro lock node."""
153+
node = Node(client, copy.deepcopy(lock_august_asl03_state))
154+
client.driver.controller.nodes[node.node_id] = node
155+
return node
156+
157+
158+
@pytest.fixture(name="lock_id_lock_as_id150")
159+
def lock_id_lock_as_id150(client, lock_id_lock_as_id150_state):
160+
"""Mock an id lock id-150 lock node."""
161+
node = Node(client, copy.deepcopy(lock_id_lock_as_id150_state))
162+
client.driver.controller.nodes[node.node_id] = node
163+
return node
164+
165+
166+
@pytest.fixture(name="integration")
167+
async def integration_fixture(hass: HomeAssistant, client):
168+
"""Set up the zwave_js integration."""
169+
entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"})
170+
entry.add_to_hass(hass)
171+
await hass.config_entries.async_setup(entry.entry_id)
172+
await hass.async_block_till_done()
173+
174+
client.async_send_command.reset_mock()
175+
176+
return entry
177+
178+
179+
@pytest.fixture(name="lock_id_lock_as_id150_not_ready")
180+
def node_not_ready(client, lock_id_lock_as_id150_state):
181+
"""Mock an id lock id-150 lock node that's not ready."""
182+
state = copy.deepcopy(lock_id_lock_as_id150_state)
183+
state["ready"] = False
184+
node = Node(client, state)
185+
client.driver.controller.nodes[node.node_id] = node
186+
return node
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
{
2+
"nodeId": 1,
3+
"index": 0,
4+
"status": 4,
5+
"ready": true,
6+
"isListening": true,
7+
"isRouting": false,
8+
"isSecure": "unknown",
9+
"manufacturerId": 134,
10+
"productId": 90,
11+
"productType": 1,
12+
"firmwareVersion": "1.2",
13+
"deviceConfig": {
14+
"filename": "/data/db/devices/0x0086/zw090.json",
15+
"isEmbedded": true,
16+
"manufacturer": "AEON Labs",
17+
"manufacturerId": 134,
18+
"label": "ZW090",
19+
"description": "Z\u2010Stick Gen5 USB Controller",
20+
"devices": [
21+
{
22+
"productType": 1,
23+
"productId": 90
24+
},
25+
{
26+
"productType": 257,
27+
"productId": 90
28+
},
29+
{
30+
"productType": 513,
31+
"productId": 90
32+
}
33+
],
34+
"firmwareVersion": {
35+
"min": "0.0",
36+
"max": "255.255"
37+
},
38+
"associations": {},
39+
"paramInformation": {
40+
"_map": {}
41+
},
42+
"metadata": {
43+
"reset": "Use this procedure only in the event that the primary controller is missing or otherwise inoperable.\n\nPress and hold the Action Button on Z-Stick for 20 seconds and then release",
44+
"manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/1345/Z%20Stick%20Gen5%20manual%201.pdf"
45+
}
46+
},
47+
"label": "ZW090",
48+
"interviewAttempts": 0,
49+
"endpoints": [
50+
{
51+
"nodeId": 1,
52+
"index": 0,
53+
"deviceClass": {
54+
"basic": {
55+
"key": 2,
56+
"label": "Static Controller"
57+
},
58+
"generic": {
59+
"key": 2,
60+
"label": "Static Controller"
61+
},
62+
"specific": {
63+
"key": 1,
64+
"label": "PC Controller"
65+
},
66+
"mandatorySupportedCCs": [],
67+
"mandatoryControlledCCs": [32]
68+
},
69+
"commandClasses": []
70+
}
71+
],
72+
"values": [],
73+
"isFrequentListening": false,
74+
"maxDataRate": 40000,
75+
"supportedDataRates": [40000],
76+
"protocolVersion": 3,
77+
"deviceClass": {
78+
"basic": {
79+
"key": 2,
80+
"label": "Static Controller"
81+
},
82+
"generic": {
83+
"key": 2,
84+
"label": "Static Controller"
85+
},
86+
"specific": {
87+
"key": 1,
88+
"label": "PC Controller"
89+
},
90+
"mandatorySupportedCCs": [],
91+
"mandatoryControlledCCs": [32]
92+
},
93+
"interviewStage": "Complete",
94+
"deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x0086:0x0001:0x005a:1.2",
95+
"statistics": {
96+
"commandsTX": 0,
97+
"commandsRX": 0,
98+
"commandsDroppedRX": 0,
99+
"commandsDroppedTX": 0,
100+
"timeoutResponse": 0
101+
},
102+
"isControllerNode": true,
103+
"keepAwake": false
104+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"controller": {
3+
"libraryVersion": "Z-Wave 3.95",
4+
"type": 1,
5+
"homeId": 3245146787,
6+
"ownNodeId": 1,
7+
"isSecondary": false,
8+
"isUsingHomeIdFromOtherNetwork": false,
9+
"isSISPresent": true,
10+
"wasRealPrimary": true,
11+
"isStaticUpdateController": true,
12+
"isSlave": false,
13+
"serialApiVersion": "1.0",
14+
"manufacturerId": 134,
15+
"productType": 257,
16+
"productId": 90,
17+
"supportedFunctionTypes": [
18+
2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 28, 32, 33,
19+
34, 35, 36, 39, 41, 42, 43, 44, 45, 65, 66, 68, 69, 70, 71, 72, 73, 74,
20+
75, 76, 77, 80, 81, 83, 84, 85, 86, 87, 94, 96, 97, 98, 99, 102, 103, 128,
21+
144, 146, 147, 152, 180, 182, 183, 184, 185, 186, 189, 190, 191, 210, 211,
22+
212, 238, 239
23+
],
24+
"sucNodeId": 1,
25+
"supportsTimers": false,
26+
"isHealNetworkActive": false,
27+
"inclusionState": 0,
28+
"status": 0
29+
},
30+
"nodes": []
31+
}

0 commit comments

Comments
 (0)