Repository: zigpy/zigpy-znp (language: python)

Issue:
Various failing tests with zigpy==0.54.0
From zigpy 0.54.0 we see a bunch of tests failing and/or running into timeouts. So I went and increased the timeout of the tests to 5 minutes and these tests are still failing. Ran the tests in parallel so I could have multiple tests timeout in parallel.

```
FAILED tests/application/test_requests.py::test_zigpy_request_failure[FormedZStack3CC2531] - zigpy_znp.exceptions.CommandNotRecognized: Fatal request error RPCError.Com...
FAILED tests/application/test_requests.py::test_zigpy_request[FormedZStack1CC2531] - zigpy_znp.exceptions.CommandNotRecognized: Fatal request error RPCError.Com...
FAILED tests/application/test_requests.py::test_zigpy_request[FormedZStack3CC2531] - zigpy_znp.exceptions.CommandNotRecognized: Fatal request error RPCError.Com...
FAILED tests/application/test_requests.py::test_zigpy_request[FormedLaunchpadCC26X2R1] - zigpy_znp.exceptions.CommandNotRecognized: Fatal request error RPCError.Com...
FAILED tests/application/test_requests.py::test_zigpy_request_failure[FormedLaunchpadCC26X2R1] - zigpy_znp.exceptions.CommandNotRecognized: Fatal request error RPCError.Com...
FAILED tests/application/test_requests.py::test_zigpy_request_failure[FormedZStack1CC2531] - zigpy_znp.exceptions.CommandNotRecognized: Fatal request error RPCError.Com...
FAILED tests/application/test_joining.py::test_join_device[FormedLaunchpadCC26X2R1] - asyncio.exceptions.TimeoutError
FAILED tests/application/test_zdo_requests.py::test_mgmt_nwk_update_req[FormedLaunchpadCC26X2R1-False-1-False] - asyncio.exceptions.TimeoutError
FAILED tests/application/test_zdo_requests.py::test_mgmt_nwk_update_req[FormedLaunchpadCC26X2R1-False-200-True] - asyncio.exceptions.TimeoutError
FAILED tests/application/test_zdo_requests.py::test_mgmt_nwk_update_req[FormedLaunchpadCC26X2R1-False-1-True] - asyncio.exceptions.TimeoutError
FAILED tests/application/test_requests.py::test_request_recovery_route_rediscovery_zdo[FormedLaunchpadCC26X2R1] - asyncio.exceptions.TimeoutError
FAILED tests/application/test_zdo_requests.py::test_mgmt_nwk_update_req[FormedLaunchpadCC26X2R1-True-1-False] - asyncio.exceptions.TimeoutError
FAILED tests/application/test_joining.py::test_permit_join[FormedLaunchpadCC26X2R1] - Failed: Timeout >300.0s
FAILED tests/application/test_joining.py::test_permit_join[FormedZStack1CC2531] - Failed: Timeout >300.0s
FAILED tests/application/test_joining.py::test_permit_join[FormedZStack3CC2531] - Failed: Timeout >300.0s
```

Full build log: https://gist.github.com/mweinelt/5ec098950ae2f0c0d49547e46660bf3e


Discussion / hints:
Looks like there are some compatibility issues with unit tests and the most recent release of zigpy.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c7e479c0..678678ab 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
           restore-keys: |
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-
       - name: Create Python virtual environment
@@ -66,7 +66,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
       - name: Fail job if Python cache restore failed
         if: steps.cache-venv.outputs.cache-hit != 'true'
         run: |
@@ -107,7 +107,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
       - name: Fail job if Python cache restore failed
         if: steps.cache-venv.outputs.cache-hit != 'true'
         run: |
@@ -150,7 +150,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
       - name: Fail job if Python cache restore failed
         if: steps.cache-venv.outputs.cache-hit != 'true'
         run: |
@@ -196,7 +196,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
       - name: Fail job if Python cache restore failed
         if: steps.cache-venv.outputs.cache-hit != 'true'
         run: |
@@ -243,7 +243,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
       - name: Fail job if Python cache restore failed
         if: steps.cache-venv.outputs.cache-hit != 'true'
         run: |
@@ -264,7 +264,7 @@ jobs:
           . venv/bin/activate
           pytest \
             -qq \
-            --timeout=20 \
+            --timeout=30 \
             --durations=10 \
             --cov zigpy_znp \
             --cov-config pyproject.toml \
@@ -298,7 +298,7 @@ jobs:
           key: >-
             ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
             steps.python.outputs.python-version }}-${{
-            hashFiles('setup.cfg') }}
+            hashFiles('pyproject.toml') }}
       - name: Fail job if Python cache restore failed
         if: steps.cache-venv.outputs.cache-hit != 'true'
         run: |
diff --git a/zigpy_znp/api.py b/zigpy_znp/api.py
index f6c2157f..25d7a762 100644
--- a/zigpy_znp/api.py
+++ b/zigpy_znp/api.py
@@ -980,8 +980,15 @@ async def request(
         if self._uart is None:
             raise RuntimeError("Coordinator is disconnected, cannot send request")
 
+        # Immediately send reset requests
+        ctx = (
+            contextlib.AsyncExitStack()
+            if isinstance(request, c.SYS.ResetReq.Req)
+            else self._sync_request_lock
+        )
+
         # We should only be sending one SREQ at a time, according to the spec
-        async with self._sync_request_lock:
+        async with ctx:
             LOGGER.debug("Sending request: %s", request)
 
             # If our request has no response, we cannot wait for one


Test patch:
diff --git a/tests/application/test_connect.py b/tests/application/test_connect.py
index 60d6e1e9..2d087cd8 100644
--- a/tests/application/test_connect.py
+++ b/tests/application/test_connect.py
@@ -120,7 +120,7 @@ async def test_probe_multiple(device, make_znp_server):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_reconnect(device, make_application):
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={
             # Make auto-reconnection happen really fast
@@ -162,7 +162,7 @@ async def test_reconnect(device, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_shutdown_from_app(device, mocker, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -178,7 +178,7 @@ async def test_shutdown_from_app(device, mocker, make_application):
 
 
 async def test_clean_shutdown(make_application):
-    app, znp_server = await make_application(server_cls=FormedLaunchpadCC26X2R1)
+    app, znp_server = make_application(server_cls=FormedLaunchpadCC26X2R1)
     await app.startup(auto_form=False)
 
     # This should not throw
@@ -189,7 +189,7 @@ async def test_clean_shutdown(make_application):
 
 
 async def test_multiple_shutdown(make_application):
-    app, znp_server = await make_application(server_cls=FormedLaunchpadCC26X2R1)
+    app, znp_server = make_application(server_cls=FormedLaunchpadCC26X2R1)
     await app.startup(auto_form=False)
 
     await app.shutdown()
@@ -201,7 +201,7 @@ async def test_multiple_shutdown(make_application):
 async def test_reconnect_lockup(device, make_application, mocker):
     mocker.patch("zigpy_znp.zigbee.application.WATCHDOG_PERIOD", 0.1)
 
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={
             # Make auto-reconnection happen really fast
@@ -242,7 +242,7 @@ async def test_reconnect_lockup(device, make_application, mocker):
 async def test_reconnect_lockup_pyserial(device, make_application, mocker):
     mocker.patch("zigpy_znp.zigbee.application.WATCHDOG_PERIOD", 0.1)
 
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={
             conf.CONF_ZNP_CONFIG: {
@@ -293,7 +293,7 @@ async def patched_start_network(old_start_network=app.start_network, **kwargs):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_disconnect(device, make_application):
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={
             conf.CONF_ZNP_CONFIG: {
@@ -316,7 +316,7 @@ async def test_disconnect(device, make_application):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_disconnect_failure(device, make_application):
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={
             conf.CONF_ZNP_CONFIG: {
diff --git a/tests/application/test_joining.py b/tests/application/test_joining.py
index 1cd30203..bfe5a9a8 100644
--- a/tests/application/test_joining.py
+++ b/tests/application/test_joining.py
@@ -1,9 +1,11 @@
 import asyncio
 import contextlib
+from unittest import mock
 
 import pytest
 import zigpy.util
 import zigpy.types
+import zigpy.device
 import zigpy.zdo.types as zdo_t
 
 import zigpy_znp.types as t
@@ -21,7 +23,7 @@
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_permit_join(device, mocker, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     permit_join_coordinator = znp_server.reply_once_to(
         request=c.ZDO.MgmtPermitJoinReq.Req(
@@ -38,7 +40,7 @@ async def test_permit_join(device, mocker, make_application):
         request=zdo_request_matcher(
             dst_addr=t.AddrModeAddress(t.AddrMode.Broadcast, 0xFFFC),
             command_id=zdo_t.ZDOCmd.Mgmt_Permit_Joining_req,
-            TSN=6,
+            TSN=7,
             zdo_PermitDuration=10,
             zdo_TC_Significant=0,
         ),
@@ -74,7 +76,7 @@ async def test_permit_join(device, mocker, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_join_coordinator(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     # Handle us opening joins on the coordinator
     permit_join_coordinator = znp_server.reply_once_to(
@@ -100,7 +102,7 @@ async def test_join_device(device, make_application):
     ieee = t.EUI64.convert("EC:1B:BD:FF:FE:54:4F:40")
     nwk = 0x1234
 
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     device = app.add_initialized_device(ieee=ieee, nwk=nwk)
 
     permit_join = znp_server.reply_once_to(
@@ -115,7 +117,7 @@ async def test_join_device(device, make_application):
                 IsBroadcast=t.Bool.false,
                 ClusterId=32822,
                 SecurityUse=0,
-                TSN=6,
+                TSN=7,
                 MacDst=0x0000,
                 Data=b"\x00",
             ),
@@ -133,7 +135,7 @@ async def test_join_device(device, make_application):
 @pytest.mark.parametrize("device", FORMED_ZSTACK3_DEVICES)
 @pytest.mark.parametrize("permit_result", [None, asyncio.TimeoutError()])
 async def test_permit_join_with_key(device, permit_result, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     # Consciot bulb
     ieee = t.EUI64.convert("EC:1B:BD:FF:FE:54:4F:40")
@@ -183,7 +185,7 @@ async def test_permit_join_with_key(device, permit_result, make_application, moc
 
 @pytest.mark.parametrize("device", FORMED_ZSTACK3_DEVICES)
 async def test_permit_join_with_invalid_key(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     # Consciot bulb
     ieee = t.EUI64.convert("EC:1B:BD:FF:FE:54:4F:40")
@@ -197,10 +199,10 @@ async def test_permit_join_with_invalid_key(device, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_zdo_device_join(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
-    mocker.patch.object(app, "handle_join")
+    mocker.patch.object(app, "handle_join", wraps=app.handle_join)
     mocker.patch("zigpy_znp.zigbee.application.DEVICE_JOIN_MAX_DELAY", new=0)
 
     nwk = 0x1234
@@ -217,10 +219,10 @@ async def test_on_zdo_device_join(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_zdo_device_join_and_announce_fast(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
-    mocker.patch.object(app, "handle_join")
+    mocker.patch.object(app, "handle_join", wraps=app.handle_join)
     mocker.patch("zigpy_znp.zigbee.application.DEVICE_JOIN_MAX_DELAY", new=0.5)
 
     nwk = 0x1234
@@ -272,9 +274,14 @@ async def test_on_zdo_device_join_and_announce_fast(device, make_application, mo
     await app.shutdown()
 
 
+@mock.patch("zigpy_znp.zigbee.application.DEVICE_JOIN_MAX_DELAY", new=0.1)
+@mock.patch(
+    "zigpy.device.Device._initialize",
+    new=zigpy.device.Device._initialize.__wrapped__,  # to disable retries
+)
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_zdo_device_join_and_announce_slow(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     znp_server.reply_to(
@@ -282,8 +289,7 @@ async def test_on_zdo_device_join_and_announce_slow(device, make_application, mo
         responses=[c.ZDO.ExtRouteDisc.Rsp(Status=t.Status.SUCCESS)],
     )
 
-    mocker.patch.object(app, "handle_join")
-    mocker.patch("zigpy_znp.zigbee.application.DEVICE_JOIN_MAX_DELAY", new=0.1)
+    mocker.patch.object(app, "handle_join", wraps=app.handle_join)
 
     nwk = 0x1234
     ieee = t.EUI64.convert("11:22:33:44:55:66:77:88")
@@ -295,11 +301,13 @@ async def test_on_zdo_device_join_and_announce_slow(device, make_application, mo
     # We're waiting for the device to announce itself
     assert app.handle_join.call_count == 0
 
-    await asyncio.sleep(0.3)
+    # Wait for the trust center join timeout to elapse
+    while app.handle_join.call_count == 0:
+        await asyncio.sleep(0.1)
 
-    # Too late, it already happened
     app.handle_join.assert_called_once_with(nwk=nwk, ieee=ieee, parent_nwk=0x0001)
 
+    # Finally, send the device announcement
     znp_server.send(
         c.ZDO.MsgCbIncoming.Callback(
             Src=nwk,
@@ -327,9 +335,10 @@ async def test_on_zdo_device_join_and_announce_slow(device, make_application, mo
         )
     )
 
-    await asyncio.sleep(0.1)
+    await asyncio.sleep(0.5)
 
     # The announcement will trigger another join indication
     assert app.handle_join.call_count == 2
 
+    app.get_device(ieee=ieee).cancel_initialization()
     await app.shutdown()
diff --git a/tests/application/test_requests.py b/tests/application/test_requests.py
index 80bb9afe..555bd79f 100644
--- a/tests/application/test_requests.py
+++ b/tests/application/test_requests.py
@@ -23,7 +23,7 @@
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_chosen_dst_endpoint(device, make_application, mocker):
-    app, znp_server = await make_application(device)
+    app, znp_server = make_application(device)
     await app.startup(auto_form=False)
 
     build = mocker.patch.object(type(app), "_zstack_build_id", mocker.PropertyMock())
@@ -48,10 +48,10 @@ async def test_chosen_dst_endpoint(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_zigpy_request(device, make_application):
-    app, znp_server = await make_application(device)
+    app, znp_server = make_application(device)
     await app.startup(auto_form=False)
 
-    TSN = 6
+    TSN = 7
 
     device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xAABB)
 
@@ -108,10 +108,10 @@ async def test_zigpy_request(device, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_zigpy_request_failure(device, make_application, mocker):
-    app, znp_server = await make_application(device)
+    app, znp_server = make_application(device)
     await app.startup(auto_form=False)
 
-    TSN = 6
+    TSN = 7
 
     device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xAABB)
 
@@ -161,7 +161,7 @@ async def test_zigpy_request_failure(device, make_application, mocker):
     ],
 )
 async def test_request_addr_mode(device, addr, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -188,7 +188,7 @@ async def test_request_addr_mode(device, addr, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_mrequest(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     mocker.patch.object(app, "send_packet", new=CoroutineMock())
     group = app.groups.add_group(0x1234, "test group")
@@ -207,7 +207,7 @@ async def test_mrequest(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_mrequest_doesnt_block(device, make_application, event_loop):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     znp_server.reply_once_to(
         request=c.AF.DataRequestExt.Req(
@@ -240,7 +240,7 @@ async def test_mrequest_doesnt_block(device, make_application, event_loop):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_broadcast(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup()
 
     znp_server.reply_once_to(
@@ -277,7 +277,7 @@ async def test_broadcast(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_request_concurrency(device, make_application, mocker):
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={conf.CONF_MAX_CONCURRENT_REQUESTS: 2},
     )
@@ -344,7 +344,7 @@ async def callback(req):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_nonstandard_profile(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xFA9E)
@@ -401,7 +401,7 @@ async def test_nonstandard_profile(device, make_application):
 async def test_request_cancellation_shielding(
     device, make_application, mocker, event_loop
 ):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -456,9 +456,9 @@ async def inner():
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_request_recovery_route_rediscovery_zdo(device, make_application, mocker):
-    TSN = 6
+    TSN = 7
 
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -539,7 +539,7 @@ def set_route_discovered(req):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_request_recovery_route_rediscovery_af(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -608,7 +608,7 @@ def set_route_discovered(req):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_request_recovery_use_ieee_addr(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -676,7 +676,7 @@ def data_confirm_replier(req):
 async def test_request_recovery_assoc_remove(
     device_cls, fw_assoc_remove, final_status, make_application, mocker
 ):
-    app, znp_server = await make_application(server_cls=device_cls)
+    app, znp_server = make_application(server_cls=device_cls)
 
     await app.startup(auto_form=False)
 
@@ -810,7 +810,7 @@ def assoc_remove(req):
 async def test_request_recovery_manual_source_route(
     device, succeed, relays, make_application, mocker
 ):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -884,7 +884,7 @@ def data_confirm_replier(req):
 
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_route_discovery_concurrency(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -921,7 +921,7 @@ async def test_route_discovery_concurrency(device, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_send_security_and_packet_source_route(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     packet = zigpy_t.ZigbeePacket(
@@ -972,7 +972,7 @@ async def test_send_security_and_packet_source_route(device, make_application, m
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_send_packet_failure(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     packet = zigpy_t.ZigbeePacket(
@@ -1013,7 +1013,7 @@ async def test_send_packet_failure(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_send_packet_failure_disconnected(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     app._znp = None
diff --git a/tests/application/test_startup.py b/tests/application/test_startup.py
index 7646a273..ce55feee 100644
--- a/tests/application/test_startup.py
+++ b/tests/application/test_startup.py
@@ -64,7 +64,7 @@ async def test_info(
     make_application,
     caplog,
 ):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     await app.startup(auto_form=False)
 
@@ -91,7 +91,7 @@ async def test_info(
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_endpoints(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     endpoints = []
     znp_server.callback_for_response(c.AF.Register.Req(partial=True), endpoints.append)
@@ -108,7 +108,7 @@ async def test_endpoints(device, make_application):
 
 @pytest.mark.parametrize("device", EMPTY_DEVICES)
 async def test_not_configured(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     # We cannot start the application if Z-Stack is not configured and without auto_form
     with pytest.raises(NetworkNotFormed):
@@ -117,7 +117,7 @@ async def test_not_configured(device, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_reset(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     # `_reset` should be called at least once to put the radio into a consistent state
     mocker.spy(ZNP, "reset")
@@ -131,7 +131,7 @@ async def test_reset(device, make_application, mocker):
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 @pytest.mark.parametrize("succeed", [True, False])
 async def test_tx_power(device, succeed, make_application):
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={conf.CONF_ZNP_CONFIG: {conf.CONF_TX_POWER: 19}},
     )
@@ -178,7 +178,7 @@ async def test_tx_power(device, succeed, make_application):
 @pytest.mark.parametrize("led_mode", ["off", False, "on", True])
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_led_mode(device, led_mode, make_application):
-    app, znp_server = await make_application(
+    app, znp_server = make_application(
         server_cls=device,
         client_config={conf.CONF_ZNP_CONFIG: {conf.CONF_LED_MODE: led_mode}},
     )
@@ -207,7 +207,7 @@ async def test_led_mode(device, led_mode, make_application):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_auto_form_unnecessary(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     mocker.patch.object(app, "form_network", new=CoroutineMock())
 
     await app.startup(auto_form=True)
@@ -219,7 +219,7 @@ async def test_auto_form_unnecessary(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", EMPTY_DEVICES)
 async def test_auto_form_necessary(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     assert app.state.network_info.channel == 0
     assert app.state.network_info.channel_mask == t.Channels.NO_CHANNELS
@@ -244,7 +244,7 @@ async def test_auto_form_necessary(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", [FormedZStack1CC2531])
 async def test_zstack_build_id_empty(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     znp_server.reply_to(
         c.SYS.Version.Req(),
@@ -273,7 +273,7 @@ async def test_zstack_build_id_empty(device, make_application, mocker):
 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
 async def test_deprecated_concurrency_config(device, make_application):
     with pytest.raises(vol.MultipleInvalid) as exc:
-        app, znp_server = await make_application(
+        app, znp_server = make_application(
             server_cls=device,
             client_config={
                 conf.CONF_ZNP_CONFIG: {
@@ -287,7 +287,7 @@ async def test_deprecated_concurrency_config(device, make_application):
 
 @pytest.mark.parametrize("device", ALL_DEVICES)
 async def test_reset_network_info(device, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.connect()
     await app.reset_network_info()
 
@@ -303,7 +303,7 @@ async def test_reset_network_info(device, make_application):
     ],
 )
 async def test_concurrency_auto_config(device, concurrency, make_application):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.connect()
     await app.start_network()
 
diff --git a/tests/application/test_zdo_requests.py b/tests/application/test_zdo_requests.py
index b6a0b82d..f2ec7733 100644
--- a/tests/application/test_zdo_requests.py
+++ b/tests/application/test_zdo_requests.py
@@ -26,7 +26,7 @@ async def test_mgmt_nwk_update_req(
 ):
     mocker.patch("zigpy_znp.zigbee.device.NWK_UPDATE_LOOP_DELAY", 0.1)
 
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
 
     if change_channel:
         new_channel = 11 + (26 - znp_server.nib.nwkLogicalChannel)
diff --git a/tests/application/test_zigpy_callbacks.py b/tests/application/test_zigpy_callbacks.py
index 265f44c6..c8465e72 100644
--- a/tests/application/test_zigpy_callbacks.py
+++ b/tests/application/test_zigpy_callbacks.py
@@ -13,7 +13,7 @@
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_zdo_relays_message_callback(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     app.handle_relays = MagicMock()
@@ -28,7 +28,7 @@ async def test_on_zdo_relays_message_callback(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_zdo_device_announce_nwk_change(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     mocker.spy(app, "handle_join")
@@ -79,7 +79,7 @@ async def test_on_zdo_device_announce_nwk_change(device, make_application, mocke
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_zdo_device_leave_callback(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     mocker.patch.object(app, "handle_leave")
@@ -99,7 +99,7 @@ async def test_on_zdo_device_leave_callback(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_on_af_message_callback(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     mocker.patch.object(app, "handle_message")
@@ -172,7 +172,7 @@ async def test_on_af_message_callback(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_receive_zdo_broadcast(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     mocker.patch.object(app, "packet_received")
@@ -209,7 +209,7 @@ async def test_receive_zdo_broadcast(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_receive_af_broadcast(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     mocker.patch.object(app, "packet_received")
@@ -254,7 +254,7 @@ async def test_receive_af_broadcast(device, make_application, mocker):
 
 @pytest.mark.parametrize("device", FORMED_DEVICES)
 async def test_receive_af_group(device, make_application, mocker):
-    app, znp_server = await make_application(server_cls=device)
+    app, znp_server = make_application(server_cls=device)
     await app.startup(auto_form=False)
 
     mocker.patch.object(app, "packet_received")
diff --git a/tests/conftest.py b/tests/conftest.py
index ad14d6ce..530e9dba 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -217,7 +217,7 @@ def merge_dicts(a, b):
 
 @pytest.fixture
 def make_application(make_znp_server):
-    async def inner(
+    def inner(
         server_cls,
         client_config=None,
         server_config=None,
@@ -615,6 +615,35 @@ def on_zdo_mgmt_permit_joining_req(self, req, PermitDuration, TC_Significant):
 
         return responses
 
+    def on_zdo_mgmt_nwk_update_req(self, req, NwkUpdate):
+        # Only handle energy scanning, for now
+        if NwkUpdate.ScanDuration in (
+            zdo_t.NwkUpdate.CHANNEL_CHANGE_REQ,
+            zdo_t.NwkUpdate.CHANNEL_MASK_MANAGER_ADDR_CHANGE_REQ,
+        ):
+            return
+
+        responses = [
+            c.ZDO.MsgCbIncoming.Callback(
+                Src=0x0000,
+                IsBroadcast=t.Bool.false,
+                ClusterId=zdo_t.ZDOCmd.Mgmt_NWK_Update_rsp,
+                SecurityUse=0,
+                TSN=req.TSN,
+                MacDst=0x0000,
+                Data=serialize_zdo_command(
+                    command_id=zdo_t.ZDOCmd.Mgmt_NWK_Update_rsp,
+                    Status=zdo_t.Status.SUCCESS,
+                    ScannedChannels=NwkUpdate.ScanChannels,
+                    TotalTransmissions=0,
+                    TransmissionFailures=0,
+                    EnergyValues=list(NwkUpdate.ScanChannels),
+                ),
+            )
+        ]
+
+        return responses
+
     def on_zdo_active_ep_req(self, req, NWKAddrOfInterest):
         if NWKAddrOfInterest != 0x0000:
             return