@@ -23,9 +23,11 @@ def _spec(name="r", gpu=None, cpu=None):
2323 return ResourceSpec (kind = ResourceKind .TASK , name = name , gpu = gpu , cpu = cpu )
2424
2525
26- def _api (volumes = None , created = None ):
26+ def _api (volumes = None , created = None , global_volumes = None ):
2727 api = AsyncMock ()
2828 api .list_network_volumes .return_value = volumes or []
29+ api .list_global_volumes .return_value = global_volumes or []
30+ api .create_global_volume .return_value = {"id" : "gv-new" , "name" : "models" }
2931 api .create_network_volume .return_value = created or {
3032 "id" : "nv-new" ,
3133 "name" : "models" ,
@@ -174,26 +176,57 @@ def test_missing_creates_with_placement(self):
174176 assert resolved ["id" ] == "nv-new"
175177 api .create_network_volume .assert_awaited_once ()
176178
177- def test_missing_no_create_raises (self ):
179+ @pytest .mark .parametrize ("volume_type" , [NetworkVolume , GlobalVolume ])
180+ def test_missing_no_create_raises (self , volume_type ):
178181 api = _api ()
179182 resolver = VolumeResolver (api )
180183 with pytest .raises (VolumeError , match = "create=False" ):
181184 asyncio .run (
182- resolver .resolve (
183- NetworkVolume ("models" , create = False ), [_spec (gpu = None )]
184- )
185+ resolver .resolve (volume_type ("models" , create = False ), [_spec (gpu = None )])
185186 )
186187
187- def test_duplicate_names_raise (self ):
188+ @pytest .mark .parametrize ("volume_type" , [NetworkVolume , GlobalVolume ])
189+ def test_duplicate_names_raise (self , volume_type ):
188190 api = _api (
189191 volumes = [
190192 {"id" : "nv-1" , "name" : "models" , "size" : 50 , "dataCenter" : "EU-RO-1" },
191193 {"id" : "nv-2" , "name" : "models" , "size" : 50 , "dataCenter" : "US-KS-2" },
192194 ]
193195 )
196+ api .list_global_volumes .return_value = api .list_network_volumes .return_value
194197 resolver = VolumeResolver (api )
195198 with pytest .raises (VolumeError , match = "reference by id" ):
196- asyncio .run (resolver .resolve (NetworkVolume ("models" ), [_spec (gpu = None )]))
199+ asyncio .run (resolver .resolve (volume_type ("models" ), [_spec (gpu = None )]))
200+
201+ @pytest .mark .parametrize ("reference" , ["models" , "gv-1" ])
202+ def test_existing_global_resolves_without_creation_or_placement (self , reference ):
203+ api = _api (global_volumes = [{"id" : "gv-1" , "name" : "models" }])
204+ resolved = asyncio .run (
205+ VolumeResolver (api ).resolve (GlobalVolume (reference , create = False ))
206+ )
207+ assert resolved == {"id" : "gv-1" }
208+ api .create_global_volume .assert_not_awaited ()
209+ api .list_network_volumes .assert_not_awaited ()
210+
211+ def test_global_creation_is_cached_and_mounts_resolve_by_name_and_id (
212+ self , tmp_path
213+ ):
214+ (tmp_path / "model" ).write_text ("global weights" )
215+ api = _api ()
216+ resolver = VolumeResolver (api )
217+
218+ async def run ():
219+ bindings = await resolver .resolve_mounts (
220+ {str (tmp_path ): GlobalVolume ("models" )}
221+ )
222+ assert await resolver .resolve (GlobalVolume ("models" )) == {"id" : "gv-new" }
223+ return bindings
224+
225+ _configure_mounts (asyncio .run (run ()))
226+ assert (GlobalVolume ("models" ).path / "model" ).read_text () == "global weights"
227+ assert (GlobalVolume ("gv-new" ).path / "model" ).read_text () == "global weights"
228+ api .create_global_volume .assert_awaited_once ()
229+ api .list_network_volumes .assert_not_awaited ()
197230
198231 def test_resolution_cached_per_name (self ):
199232 api = _api (
@@ -214,7 +247,7 @@ def test_same_reference_in_different_backends_does_not_share_cache(self):
214247 resolver = VolumeResolver (
215248 _api (volumes = [{"id" : "nv-1" , "name" : "models" , "dataCenter" : "EU-RO-1" }])
216249 )
217- assert asyncio .run (resolver .resolve (GlobalVolume ("models" ))) == {"id" : "models " }
250+ assert asyncio .run (resolver .resolve (GlobalVolume ("models" ))) == {"id" : "gv-new " }
218251 assert asyncio .run (resolver .resolve (NetworkVolume ("models" ))) == {
219252 "id" : "nv-1" ,
220253 "dataCenterId" : "EU-RO-1" ,
@@ -244,9 +277,10 @@ def test_pod_pins_to_volume_dc(self):
244277 from runpod .apps .tasks import TaskExecution
245278
246279 api = _api (
280+ global_volumes = [{"id" : "gv-1" , "name" : "shared-global" }],
247281 volumes = [
248282 {"id" : "nv-1" , "name" : "models" , "size" : 50 , "dataCenter" : "EU-RO-1" }
249- ]
283+ ],
250284 )
251285 spec = ResourceSpec (
252286 kind = ResourceKind .TASK ,
@@ -298,18 +332,18 @@ def test_global_attachment_does_not_pin_datacenter(self):
298332
299333 app = App ("global-model" )
300334
301- @app .queue (gpu = "4090" , mounts = {"/runpod-volume" : GlobalVolume ("gv-1 " )})
335+ @app .queue (gpu = "4090" , mounts = {"/runpod-volume" : GlobalVolume ("models " )})
302336 def generate ():
303337 return None
304338
305339 payload = {"locations" : "EU-RO-1,US-KS-2" }
306340 asyncio .run (
307- attach_endpoint_volumes (payload , generate .spec , VolumeResolver (), app )
341+ attach_endpoint_volumes (payload , generate .spec , VolumeResolver (_api () ), app )
308342 )
309343 assert payload ["locations" ] == "EU-RO-1,US-KS-2"
310344 assert payload ["networkVolumeIds" ] == []
311345 assert payload ["volumes" ] == [
312- {"volumeId" : "gv-1 " , "volumeType" : "OBJECT_STORE_VOLUME" }
346+ {"volumeId" : "gv-new " , "volumeType" : "OBJECT_STORE_VOLUME" }
313347 ]
314348
315349 def test_removing_storage_clears_backend_and_worker_bindings (self ):
0 commit comments