ucapi-framework 1.9.2__tar.gz → 1.9.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/PKG-INFO +1 -1
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/pyproject.toml +1 -1
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_discovery.py +67 -8
- ucapi_framework-1.9.4/tests/test_helpers.py +1165 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_setup.py +4 -3
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/discovery.py +24 -21
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/driver.py +11 -2
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/setup.py +61 -3
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework.egg-info/PKG-INFO +1 -1
- ucapi_framework-1.9.2/tests/test_helpers.py +0 -420
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/README.md +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/setup.cfg +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_button_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_climate_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_config.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_cover_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_device.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_driver.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_factory_pattern.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_ir_emitter_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_light_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_media_player_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_migration.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_remote_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_select_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_sensor_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_switch_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/tests/test_voice_assistant_entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/__init__.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/config.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/device.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/__init__.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/button.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/climate.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/cover.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/ir_emitter.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/light.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/media_player.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/remote.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/select.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/sensor.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/switch.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entities/voice_assistant.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/entity.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/helpers.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework/migration.py +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework.egg-info/SOURCES.txt +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework.egg-info/dependency_links.txt +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework.egg-info/requires.txt +0 -0
- {ucapi_framework-1.9.2 → ucapi_framework-1.9.4}/ucapi_framework.egg-info/top_level.txt +0 -0
|
@@ -451,28 +451,87 @@ class TestMDNSDiscovery:
|
|
|
451
451
|
|
|
452
452
|
@pytest.mark.asyncio
|
|
453
453
|
async def test_discover_with_zeroconf(self):
|
|
454
|
-
"""Test mDNS discovery with mocked zeroconf."""
|
|
454
|
+
"""Test mDNS discovery with mocked zeroconf async classes."""
|
|
455
455
|
mock_service_info = Mock()
|
|
456
456
|
mock_service_info.name = "test-device._test._tcp.local."
|
|
457
457
|
mock_service_info.server = "Test Device"
|
|
458
458
|
mock_service_info.addresses = [b"\xc0\xa8\x01\x64"] # 192.168.1.100 as bytes
|
|
459
459
|
mock_service_info.port = 8080
|
|
460
460
|
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
# ServiceListener must be a real class so Listener can inherit from it
|
|
462
|
+
class MockServiceListener:
|
|
463
|
+
pass
|
|
464
|
+
|
|
465
|
+
# Track the Listener instance created inside discover()
|
|
466
|
+
captured_listener: dict = {}
|
|
467
|
+
|
|
468
|
+
# Mock AsyncZeroconf
|
|
469
|
+
mock_aiozc = AsyncMock()
|
|
470
|
+
mock_aiozc.zeroconf = Mock()
|
|
471
|
+
mock_aiozc.async_get_service_info = AsyncMock(return_value=mock_service_info)
|
|
472
|
+
mock_aiozc.async_close = AsyncMock()
|
|
473
|
+
|
|
474
|
+
# Mock AsyncServiceBrowser – capture the listener so we can call add_service
|
|
475
|
+
def mock_browser_init(zc, service_type, listener):
|
|
476
|
+
captured_listener["obj"] = listener
|
|
477
|
+
browser = Mock()
|
|
478
|
+
browser.async_cancel = AsyncMock()
|
|
479
|
+
return browser
|
|
463
480
|
|
|
464
481
|
mock_zeroconf_module = Mock()
|
|
465
|
-
mock_zeroconf_module.
|
|
466
|
-
|
|
482
|
+
mock_zeroconf_module.ServiceListener = MockServiceListener
|
|
483
|
+
|
|
484
|
+
mock_asyncio_module = Mock()
|
|
485
|
+
mock_asyncio_module.AsyncZeroconf = Mock(return_value=mock_aiozc)
|
|
486
|
+
mock_asyncio_module.AsyncServiceBrowser = Mock(side_effect=mock_browser_init)
|
|
487
|
+
|
|
488
|
+
with (
|
|
489
|
+
patch.dict(
|
|
490
|
+
"sys.modules",
|
|
491
|
+
{
|
|
492
|
+
"zeroconf": mock_zeroconf_module,
|
|
493
|
+
"zeroconf.asyncio": mock_asyncio_module,
|
|
494
|
+
},
|
|
495
|
+
),
|
|
496
|
+
patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep,
|
|
497
|
+
):
|
|
498
|
+
|
|
499
|
+
async def trigger_discovery(*args, **kwargs):
|
|
500
|
+
listener = captured_listener.get("obj")
|
|
501
|
+
if listener:
|
|
502
|
+
listener.add_service(
|
|
503
|
+
mock_aiozc.zeroconf,
|
|
504
|
+
"_test._tcp.local.",
|
|
505
|
+
"test-device._test._tcp.local.",
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
mock_sleep.side_effect = trigger_discovery
|
|
467
509
|
|
|
468
|
-
with patch.dict("sys.modules", {"zeroconf": mock_zeroconf_module}):
|
|
469
510
|
discovery = ConcreteMDNSDiscovery(
|
|
470
511
|
service_type="_test._tcp.local.", timeout=1
|
|
471
512
|
)
|
|
472
513
|
devices = await discovery.discover()
|
|
473
514
|
|
|
474
|
-
|
|
475
|
-
assert
|
|
515
|
+
assert len(devices) == 1
|
|
516
|
+
assert devices[0].identifier == "test-device._test._tcp.local."
|
|
517
|
+
assert devices[0].name == "Test Device"
|
|
518
|
+
mock_aiozc.async_get_service_info.assert_called_once_with(
|
|
519
|
+
"_test._tcp.local.", "test-device._test._tcp.local."
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
@pytest.mark.asyncio
|
|
523
|
+
async def test_discover_handles_import_error(self):
|
|
524
|
+
"""Test that discover handles missing zeroconf gracefully."""
|
|
525
|
+
discovery = ConcreteMDNSDiscovery(service_type="_test._tcp.local.", timeout=1)
|
|
526
|
+
|
|
527
|
+
def mock_import(name, *args, **kwargs):
|
|
528
|
+
if name in ("zeroconf", "zeroconf.asyncio"):
|
|
529
|
+
raise ImportError(f"No module named '{name}'")
|
|
530
|
+
return __import__(name, *args, **kwargs)
|
|
531
|
+
|
|
532
|
+
with patch("builtins.__import__", side_effect=mock_import):
|
|
533
|
+
devices = await discovery.discover()
|
|
534
|
+
assert devices == []
|
|
476
535
|
|
|
477
536
|
|
|
478
537
|
class TestNetworkScanDiscovery:
|