1717Tests the conditional logic added to support Zenoh alongside LCM:
1818- GlobalConfig transport field
1919- _get_transport_for() branching
20- - LCM configurator gating
20+ - LCM system configurators always run with blueprint checks (LCM is used outside transport_map)
21+
22+ Requires the ``zenoh`` extra (``eclipse-zenoh``): ``ZenohTransport`` is only defined
23+ when that dependency is installed, so this module does not load without it.
2124"""
2225
2326from __future__ import annotations
2427
28+ import threading
2529from typing import cast
2630
31+ import numpy as np
2732from pydantic import ValidationError
2833import pytest
2934
35+ from dimos .constants import ZENOH_DIMOS_KEY_PREFIX
3036from dimos .core .coordination .blueprints import autoconnect
3137from dimos .core .coordination .module_coordinator import _get_transport_for , _run_configurators
3238from dimos .core .global_config import GlobalConfig , global_config
3339from dimos .core .module import Module
3440from dimos .core .stream import In , Out
3541from dimos .core .test_utils import retry_until
36- from dimos .core .transport import ZENOH_AVAILABLE , LCMTransport , pLCMTransport
42+ from dimos .core .transport import (
43+ ZENOH_AVAILABLE ,
44+ LCMTransport ,
45+ ZenohTransport ,
46+ pLCMTransport ,
47+ pZenohTransport ,
48+ )
3749from dimos .msgs .sensor_msgs .Image import Image
50+ from dimos .protocol .service .zenohservice import close_all_zenoh_sessions
3851
3952
4053class TypedMsg :
@@ -102,10 +115,7 @@ class TestZenohAvailableGuard:
102115 def test_zenoh_available_is_bool (self ) -> None :
103116 assert isinstance (ZENOH_AVAILABLE , bool )
104117
105- @pytest .mark .skipif (not ZENOH_AVAILABLE , reason = "zenoh not installed" )
106- def test_zenoh_transport_classes_exist_when_available (self ) -> None :
107- from dimos .core .transport import ZenohTransport , pZenohTransport
108-
118+ def test_zenoh_transport_classes_exist (self ) -> None :
109119 assert ZenohTransport is not None
110120 assert pZenohTransport is not None
111121
@@ -128,33 +138,24 @@ def test_lcm_pickle_transport_returned_for_untyped_when_lcm(self, mocker) -> Non
128138 transport = _get_transport_for (bp , "untyped_data" , UntypedMsg )
129139 assert isinstance (transport , pLCMTransport )
130140
131- @pytest .mark .skipif (not ZENOH_AVAILABLE , reason = "zenoh not installed" )
132141 def test_zenoh_transport_returned_when_transport_is_zenoh (self , mocker ) -> None :
133- from dimos .core .transport import ZenohTransport
134-
135142 mocker .patch .object (global_config , "transport" , "zenoh" )
136143 bp = self ._make_blueprint ()
137144 transport = _get_transport_for (bp , "typed_data" , TypedMsg )
138145 assert isinstance (transport , ZenohTransport )
139146
140- @pytest .mark .skipif (not ZENOH_AVAILABLE , reason = "zenoh not installed" )
141147 def test_zenoh_pickle_transport_returned_for_untyped_when_zenoh (self , mocker ) -> None :
142- from dimos .core .transport import pZenohTransport
143-
144148 mocker .patch .object (global_config , "transport" , "zenoh" )
145149 bp = self ._make_blueprint ()
146150 transport = _get_transport_for (bp , "untyped_data" , UntypedMsg )
147151 assert isinstance (transport , pZenohTransport )
148152
149- @pytest .mark .skipif (not ZENOH_AVAILABLE , reason = "zenoh not installed" )
150153 def test_zenoh_topic_uses_dimos_prefix (self , mocker ) -> None :
151- from dimos .core .transport import pZenohTransport
152-
153154 mocker .patch .object (global_config , "transport" , "zenoh" )
154155 bp = self ._make_blueprint ()
155156 transport = _get_transport_for (bp , "untyped_data" , UntypedMsg )
156157 assert isinstance (transport , pZenohTransport )
157- assert "dimos /" in transport .topic
158+ assert f" { ZENOH_DIMOS_KEY_PREFIX } /" in transport .topic
158159
159160 def test_zenoh_raises_when_not_available (self , mocker ) -> None :
160161 mocker .patch .object (global_config , "transport" , "zenoh" )
@@ -179,7 +180,7 @@ def test_lcm_configurators_run_when_transport_is_lcm(self, mocker) -> None:
179180
180181 mock_lcm_configs .assert_called_once ()
181182
182- def test_lcm_configurators_skipped_when_transport_is_zenoh (self , mocker ) -> None :
183+ def test_lcm_configurators_run_when_transport_is_zenoh (self , mocker ) -> None :
183184 mocker .patch .object (global_config , "transport" , "zenoh" )
184185 mock_lcm_configs = mocker .patch (
185186 "dimos.protocol.service.system_configurator.lcm_config.lcm_configurators" ,
@@ -190,30 +191,19 @@ def test_lcm_configurators_skipped_when_transport_is_zenoh(self, mocker) -> None
190191 bp = autoconnect (ProducerModule .blueprint (), ConsumerModule .blueprint ())
191192 _run_configurators (bp )
192193
193- mock_lcm_configs .assert_not_called ()
194+ mock_lcm_configs .assert_called_once ()
194195
195196
196- @pytest .mark .skipif (not ZENOH_AVAILABLE , reason = "zenoh not installed" )
197197class TestZenohTransportWrapper :
198198 """Test ZenohTransport and pZenohTransport broadcast/subscribe lifecycle."""
199199
200200 @pytest .fixture (autouse = True )
201201 def _clean_sessions (self ):
202- from dimos .protocol .service .zenohservice import _sessions
203-
204202 yield
205- for s in _sessions .values ():
206- s .close ()
207- _sessions .clear ()
203+ close_all_zenoh_sessions ()
208204
209205 def test_zenoh_transport_broadcast_and_subscribe (self ) -> None :
210- import threading
211-
212- import numpy as np
213-
214- from dimos .core .transport import ZenohTransport
215-
216- t = ZenohTransport ("dimos/test/transport" , Image )
206+ t = ZenohTransport (f"{ ZENOH_DIMOS_KEY_PREFIX } /test/transport" , Image )
217207 t .start ()
218208
219209 received = []
@@ -230,11 +220,7 @@ def cb(msg): # type: ignore[no-untyped-def]
230220 t .stop ()
231221
232222 def test_pzenoh_transport_broadcast_and_subscribe (self ) -> None :
233- import threading
234-
235- from dimos .core .transport import pZenohTransport
236-
237- t = pZenohTransport ("dimos/test/pickle_transport" )
223+ t = pZenohTransport (f"{ ZENOH_DIMOS_KEY_PREFIX } /test/pickle_transport" )
238224 t .start ()
239225
240226 received = []
@@ -250,18 +236,14 @@ def cb(msg): # type: ignore[no-untyped-def]
250236 t .stop ()
251237
252238 def test_auto_start_on_broadcast (self ) -> None :
253- from dimos .core .transport import pZenohTransport
254-
255- t = pZenohTransport ("dimos/test/autostart" )
239+ t = pZenohTransport (f"{ ZENOH_DIMOS_KEY_PREFIX } /test/autostart" )
256240 # Don't call start() — broadcast should auto-start
257241 t .broadcast (None , "test" )
258242 assert t ._started
259243 t .stop ()
260244
261245 def test_stop_and_restart (self ) -> None :
262- from dimos .core .transport import pZenohTransport
263-
264- t = pZenohTransport ("dimos/test/restart" )
246+ t = pZenohTransport (f"{ ZENOH_DIMOS_KEY_PREFIX } /test/restart" )
265247 t .start ()
266248 assert t ._started
267249 t .stop ()
0 commit comments