@@ -304,3 +304,76 @@ async def test_async_discover_devices_cancelled(mock_datagram_endpoint):
304304
305305 assert "cannot_connect" in str (excinfo .value )
306306 mock_transport .close .assert_called_once ()
307+
308+
309+ @pytest .mark .asyncio
310+ async def test_datagram_received_handles_general_exception ():
311+ """Test datagram_received handles a generic exception during parsing."""
312+ mock_callback = AsyncMock ()
313+ protocol = AirOSDiscoveryProtocol (mock_callback )
314+ some_data = b"\x01 \x06 \x00 \x00 \x00 \x00 "
315+ host_ip = "192.168.1.100"
316+
317+ with (
318+ patch .object (
319+ protocol , "parse_airos_packet" , side_effect = ValueError ("A generic error" )
320+ ) as mock_parse ,
321+ patch ("airos.discovery._LOGGER.exception" ) as mock_log_exception ,
322+ ):
323+ # A generic exception should be caught and re-raised as AirOSDiscoveryError
324+ with pytest .raises (AirOSDiscoveryError ):
325+ protocol .datagram_received (some_data , (host_ip , DISCOVERY_PORT ))
326+
327+ mock_parse .assert_called_once_with (some_data , host_ip )
328+ mock_callback .assert_not_called ()
329+ mock_log_exception .assert_called_once ()
330+ assert (
331+ "Error processing AirOS discovery packet"
332+ in mock_log_exception .call_args [0 ][0 ]
333+ )
334+
335+
336+ @pytest .mark .parametrize (
337+ "packet_fragment, error_message" ,
338+ [
339+ # Case 1: TLV type 0x0A (Uptime) with wrong length
340+ (b"\x0a \x00 \x02 \x01 \x02 " , "Unexpected length for Uptime (Type 0x0A)" ),
341+ # Case 2: TLV declared length exceeds remaining packet data
342+ (b"\x0c \x00 \xff \x41 \x42 " , "length 255 exceeds remaining data" ),
343+ # Case 3: An unknown TLV type
344+ (b"\xff \x01 \x02 " , "Unhandled TLV type: 0xff" ),
345+ ],
346+ )
347+ @pytest .mark .asyncio
348+ async def test_parse_airos_packet_tlv_edge_cases (packet_fragment , error_message ):
349+ """Test parsing of various malformed TLV entries."""
350+ protocol = AirOSDiscoveryProtocol (AsyncMock ())
351+ # A valid header is required to get to the TLV parsing stage
352+ base_packet = b"\x01 \x06 \x00 \x00 \x00 \x00 "
353+ malformed_packet = base_packet + packet_fragment
354+ host_ip = "192.168.1.100"
355+
356+ with pytest .raises (AirOSEndpointError ) as excinfo :
357+ protocol .parse_airos_packet (malformed_packet , host_ip )
358+
359+ assert error_message in str (excinfo .value )
360+
361+
362+ @pytest .mark .asyncio
363+ async def test_async_discover_devices_generic_oserror (mock_datagram_endpoint ):
364+ """Test discovery handles a generic OSError during endpoint creation."""
365+ mock_transport , _ = mock_datagram_endpoint
366+
367+ with (
368+ patch ("asyncio.get_running_loop" ) as mock_get_loop ,
369+ pytest .raises (AirOSEndpointError ) as excinfo ,
370+ ):
371+ mock_loop = mock_get_loop .return_value
372+ # Simulate an OSError that is NOT 'address in use'
373+ mock_loop .create_datagram_endpoint = AsyncMock (
374+ side_effect = OSError (13 , "Permission denied" )
375+ )
376+ await async_discover_devices (timeout = 1 )
377+
378+ assert "cannot_connect" in str (excinfo .value )
379+ mock_transport .close .assert_not_called ()
0 commit comments