|
6 | 6 | from jsonschema import ValidationError
|
7 | 7 |
|
8 | 8 | import zigpy_znp.types as t
|
9 |
| -import zigpy_znp.config as conf |
10 |
| -from zigpy_znp.api import ZNP |
11 | 9 | from zigpy_znp.znp import security
|
12 | 10 | from zigpy_znp.types.nvids import ExNvIds, OsalNvIds
|
13 | 11 | from zigpy_znp.tools.common import validate_backup_json
|
14 |
| -from zigpy_znp.zigbee.application import ControllerApplication |
15 | 12 | from zigpy_znp.tools.network_backup import main as network_backup
|
16 | 13 | from zigpy_znp.tools.network_restore import main as network_restore
|
17 | 14 |
|
18 | 15 | from ..conftest import (
|
19 | 16 | ALL_DEVICES,
|
20 | 17 | EMPTY_DEVICES,
|
21 | 18 | FORMED_DEVICES,
|
22 |
| - CoroutineMock, |
23 | 19 | BaseZStack1CC2531,
|
24 | 20 | BaseZStack3CC2531,
|
25 | 21 | FormedZStack1CC2531,
|
@@ -177,135 +173,41 @@ async def test_network_backup_formed(device, make_znp_server, tmp_path):
|
177 | 173 | assert len(backup["devices"]) > 1
|
178 | 174 |
|
179 | 175 |
|
180 |
| -@pytest.mark.parametrize("device", FORMED_DEVICES) |
181 |
| -@pytest.mark.asyncio |
182 |
| -async def test_network_restore_full( |
183 |
| - device, make_znp_server, backup_json, tmp_path, mocker |
184 |
| -): |
185 |
| - backup_file = tmp_path / "backup.json" |
186 |
| - backup_file.write_text(json.dumps(backup_json)) |
187 |
| - |
188 |
| - znp_server = make_znp_server(server_cls=device) |
189 |
| - |
190 |
| - # Perform the "restore" |
191 |
| - await network_restore([znp_server._port_path, "-i", str(backup_file), "-c", "2500"]) |
192 |
| - |
193 |
| - |
194 | 176 | @pytest.mark.parametrize("device", ALL_DEVICES)
|
195 | 177 | @pytest.mark.asyncio
|
196 |
| -async def test_network_restore(device, make_znp_server, backup_json, tmp_path, mocker): |
| 178 | +async def test_network_restore_and_backup( |
| 179 | + device, make_znp_server, backup_json, tmp_path |
| 180 | +): |
197 | 181 | backup_file = tmp_path / "backup.json"
|
198 | 182 | backup_file.write_text(json.dumps(backup_json))
|
199 | 183 |
|
200 | 184 | znp_server = make_znp_server(server_cls=device)
|
201 | 185 |
|
202 |
| - async def mock_startup(self, *, force_form): |
203 |
| - assert force_form |
204 |
| - |
205 |
| - config = self.config[conf.CONF_NWK] |
206 |
| - |
207 |
| - assert config[conf.CONF_NWK_KEY] == t.KeyData( |
208 |
| - bytes.fromhex("37668fd64e35e03342e5ef9f35ccf4ab") |
209 |
| - ) |
210 |
| - assert config[conf.CONF_NWK_PAN_ID] == 0xFEED |
211 |
| - assert config[conf.CONF_NWK_CHANNEL] == 25 |
212 |
| - assert config[conf.CONF_NWK_EXTENDED_PAN_ID] == t.EUI64.convert( |
213 |
| - "ab:de:fa:bc:de:fa:bc:de" |
214 |
| - ) |
215 |
| - |
216 |
| - znp = ZNP(self.config) |
217 |
| - await znp.connect() |
218 |
| - |
219 |
| - if OsalNvIds.APS_LINK_KEY_TABLE not in znp_server._nvram[ExNvIds.LEGACY]: |
220 |
| - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.APS_LINK_KEY_TABLE] = ( |
221 |
| - b"\x00" * 1000 |
222 |
| - ) |
223 |
| - |
224 |
| - if OsalNvIds.NIB not in znp_server._nvram[ExNvIds.LEGACY]: |
225 |
| - znp_server._nvram[ExNvIds.LEGACY][ |
226 |
| - OsalNvIds.NIB |
227 |
| - ] = znp_server.nvram_serialize(znp_server._default_nib()) |
228 |
| - |
229 |
| - self._znp = znp |
230 |
| - self._znp.set_application(self) |
231 |
| - |
232 |
| - self._bind_callbacks() |
233 |
| - |
234 |
| - startup_mock = mocker.patch.object( |
235 |
| - ControllerApplication, "startup", side_effect=mock_startup, autospec=True |
236 |
| - ) |
237 |
| - |
238 |
| - async def mock_load_network_info(self, *args, **kwargs): |
239 |
| - self.network_info = BARE_NETWORK_INFO |
240 |
| - self.node_info = BARE_NODE_INFO |
241 |
| - |
242 |
| - load_nwk_info_mock = mocker.patch.object( |
243 |
| - ZNP, "load_network_info", side_effect=mock_load_network_info, autospec=True |
244 |
| - ) |
245 |
| - |
246 |
| - write_tc_counter_mock = mocker.patch( |
247 |
| - "zigpy_znp.tools.network_restore.write_tc_frame_counter", new=CoroutineMock() |
248 |
| - ) |
249 |
| - write_devices_mock = mocker.patch( |
250 |
| - "zigpy_znp.tools.network_restore.write_devices", new=CoroutineMock() |
251 |
| - ) |
252 |
| - |
253 |
| - # Perform the "restore" |
| 186 | + # Restore our backup on top of an existing network (or onto an empty device) |
254 | 187 | await network_restore([znp_server._port_path, "-i", str(backup_file), "-c", "2500"])
|
255 | 188 |
|
256 |
| - # The NIB should contain correct values |
257 |
| - nib = znp_server.nvram_deserialize( |
258 |
| - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.NIB], t.NIB |
259 |
| - ) |
260 |
| - assert nib.channelList == t.Channels.from_channel_list([15, 20, 25]) |
261 |
| - assert nib.nwkUpdateId == 2 |
262 |
| - assert nib.SecurityLevel == 5 |
| 189 | + # And then back that restored device up |
| 190 | + backup_file2 = tmp_path / "backup2.json" |
| 191 | + await network_backup([znp_server._port_path, "-o", str(backup_file2)]) |
263 | 192 |
|
264 |
| - # And validate that the low-level functions were called appropriately |
265 |
| - assert startup_mock.call_count == 1 |
266 |
| - assert startup_mock.mock_calls[0][2]["force_form"] is True |
| 193 | + backup_json2 = json.loads(backup_file2.read_text()) |
267 | 194 |
|
268 |
| - assert load_nwk_info_mock.call_count == 1 |
| 195 | + # Fix up some inconsequential metadata |
| 196 | + backup_json2["metadata"]["internal"] = backup_json["metadata"]["internal"] |
| 197 | + backup_json2["metadata"]["source"] = backup_json["metadata"]["source"] |
| 198 | + backup_json2["network_key"]["frame_counter"] -= 2500 |
269 | 199 |
|
270 |
| - assert write_tc_counter_mock.call_count == 1 |
271 |
| - assert write_tc_counter_mock.mock_calls[0][1][1] == 66781 + 2500 |
| 200 | + if issubclass(device, BaseZStack1CC2531): |
| 201 | + del backup_json["stack_specific"]["zstack"]["tclk_seed"] |
272 | 202 |
|
273 |
| - assert write_devices_mock.call_count == 1 |
274 |
| - write_devices_call = write_devices_mock.mock_calls[0] |
| 203 | + if not backup_json["stack_specific"]["zstack"]: |
| 204 | + del backup_json["stack_specific"] |
275 | 205 |
|
276 |
| - assert write_devices_call[2]["counter_increment"] == 2500 |
| 206 | + for device in backup_json["devices"]: |
| 207 | + if "link_key" in device: |
| 208 | + del device["link_key"] |
277 | 209 |
|
278 |
| - if issubclass(device, BaseZStack1CC2531): |
279 |
| - assert write_devices_call[2]["tclk_seed"] is None |
280 |
| - else: |
281 |
| - assert write_devices_call[2]["tclk_seed"] == bytes.fromhex( |
282 |
| - "c04884427c8a1ed7bb8412815ccce7aa" |
283 |
| - ) |
284 |
| - |
285 |
| - assert sorted(write_devices_call[1][1], key=lambda d: d.nwk) == [ |
286 |
| - security.StoredDevice( |
287 |
| - ieee=t.EUI64.convert("00:0b:57:ff:fe:38:b2:12"), |
288 |
| - nwk=0x9672, |
289 |
| - aps_link_key=t.KeyData.deserialize( |
290 |
| - bytes.fromhex("d2fabcbc83dd15d7a9362a7fa39becaa") |
291 |
| - )[0], |
292 |
| - rx_counter=123, |
293 |
| - tx_counter=456, |
294 |
| - ), |
295 |
| - security.StoredDevice( |
296 |
| - ieee=t.EUI64.convert("aa:bb:cc:dd:ee:ff:00:11"), |
297 |
| - nwk=0xABCD, |
298 |
| - aps_link_key=t.KeyData.deserialize( |
299 |
| - bytes.fromhex("01234567801234567801234567801234") |
300 |
| - )[0], |
301 |
| - rx_counter=112233, |
302 |
| - tx_counter=445566, |
303 |
| - ), |
304 |
| - security.StoredDevice( |
305 |
| - ieee=t.EUI64.convert("00:0b:57:ff:fe:36:b9:a0"), |
306 |
| - nwk=0xF319, |
307 |
| - ), |
308 |
| - ] |
| 210 | + assert backup_json == backup_json2 |
309 | 211 |
|
310 | 212 |
|
311 | 213 | @pytest.mark.asyncio
|
|
0 commit comments