1313
1414from dstack ._internal .core .backends .oci import region as oci_region
1515from dstack ._internal .core .models .backends .base import BackendType
16- from dstack ._internal .core .models .instances import InstanceStatus
16+ from dstack ._internal .core .models .instances import (
17+ Gpu ,
18+ InstanceOffer ,
19+ InstanceStatus ,
20+ InstanceType ,
21+ Resources ,
22+ )
1723from dstack ._internal .core .models .users import GlobalRole , ProjectRole
1824from dstack ._internal .core .models .volumes import VolumeStatus
1925from dstack ._internal .server .models import BackendModel
@@ -212,6 +218,30 @@ async def test_creates_lambda_backend(
212218 @pytest .mark .skipif (sys .version_info < (3 , 10 ), reason = "Nebius requires Python 3.10" )
213219 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
214220 class TestNebius :
221+ @pytest .fixture (autouse = True )
222+ def patch_catalog (self ):
223+ with patch (
224+ "dstack._internal.core.backends.nebius.resources.get_catalog_offers"
225+ ) as get_catalog_offers_mock :
226+ get_catalog_offers_mock .return_value = [
227+ InstanceOffer (
228+ backend = BackendType .NEBIUS ,
229+ instance = InstanceType (
230+ name = "gpu-h100-sxm 8gpu-128vcpu-1600gb" ,
231+ resources = Resources (
232+ cpus = 128 ,
233+ memory_mib = 1600 * 1024 ,
234+ gpus = [Gpu (name = "H100" , memory_mib = 80 * 1024 )] * 8 ,
235+ spot = False ,
236+ ),
237+ ),
238+ region = "eu-north1" ,
239+ price = 23.6 ,
240+ backend_data = {"fabrics" : ["fabric-2" , "fabric-3" ]},
241+ )
242+ ]
243+ yield
244+
215245 async def test_not_creates_with_invalid_creds (
216246 self , test_db , session : AsyncSession , client : AsyncClient
217247 ):
@@ -238,18 +268,16 @@ async def test_not_creates_with_invalid_creds(
238268 assert len (res .scalars ().all ()) == 0
239269
240270 @pytest .mark .parametrize (
241- ("config_regions" , "config_projects " , "mocked_projects" , "error" ),
271+ ("config_extra " , "mocked_projects" , "error" ),
242272 [
243273 pytest .param (
244- None ,
245- None ,
274+ {},
246275 [_nebius_project ()],
247276 None ,
248277 id = "default" ,
249278 ),
250279 pytest .param (
251- ["eu-north1" ],
252- None ,
280+ {"regions" : ["eu-north1" ]},
253281 [
254282 _nebius_project (
255283 "project-e00test" , "default-project-eu-north1" , "eu-north1"
@@ -260,15 +288,13 @@ async def test_not_creates_with_invalid_creds(
260288 id = "with-regions" ,
261289 ),
262290 pytest .param (
263- ["xx-xxxx1" ],
264- None ,
291+ {"regions" : ["xx-xxxx1" ]},
265292 [_nebius_project ()],
266293 "do not exist in this Nebius tenancy" ,
267294 id = "error-invalid-regions" ,
268295 ),
269296 pytest .param (
270- ["eu-north1" ],
271- None ,
297+ {"regions" : ["eu-north1" ]},
272298 [
273299 _nebius_project (
274300 "project-e00test0" , "default-project-eu-north1" , "eu-north1"
@@ -279,8 +305,7 @@ async def test_not_creates_with_invalid_creds(
279305 id = "finds-default-project-among-many" ,
280306 ),
281307 pytest .param (
282- ["eu-north1" ],
283- None ,
308+ {"regions" : ["eu-north1" ]},
284309 [
285310 _nebius_project ("project-e00test0" , "non-default-project-0" , "eu-north1" ),
286311 _nebius_project ("project-e00test1" , "non-default-project-1" , "eu-north1" ),
@@ -289,8 +314,7 @@ async def test_not_creates_with_invalid_creds(
289314 id = "error-no-default-project" ,
290315 ),
291316 pytest .param (
292- None ,
293- ["project-e00test0" ],
317+ {"projects" : ["project-e00test0" ]},
294318 [
295319 _nebius_project ("project-e00test0" , "non-default-project-0" , "eu-north1" ),
296320 _nebius_project ("project-e00test1" , "non-default-project-1" , "eu-north1" ),
@@ -299,15 +323,13 @@ async def test_not_creates_with_invalid_creds(
299323 id = "with-projects" ,
300324 ),
301325 pytest .param (
302- None ,
303- ["project-e00xxxx" ],
326+ {"projects" : ["project-e00xxxx" ]},
304327 [_nebius_project ()],
305328 "not found in this Nebius tenancy" ,
306329 id = "error-invalid-projects" ,
307330 ),
308331 pytest .param (
309- None ,
310- ["project-e00test0" , "project-e00test1" ],
332+ {"projects" : ["project-e00test0" , "project-e00test1" ]},
311333 [
312334 _nebius_project ("project-e00test0" , "non-default-project-0" , "eu-north1" ),
313335 _nebius_project ("project-e00test1" , "non-default-project-1" , "eu-north1" ),
@@ -316,8 +338,10 @@ async def test_not_creates_with_invalid_creds(
316338 id = "error-multiple-projects-in-same-region" ,
317339 ),
318340 pytest .param (
319- ["eu-north1" ],
320- ["project-e00test" ],
341+ {
342+ "regions" : ["eu-north1" ],
343+ "projects" : ["project-e00test" ],
344+ },
321345 [
322346 _nebius_project (
323347 "project-e00test" , "default-project-eu-north1" , "eu-north1"
@@ -327,15 +351,26 @@ async def test_not_creates_with_invalid_creds(
327351 None ,
328352 id = "with-regions-and-projects" ,
329353 ),
354+ pytest .param (
355+ {"fabrics" : ["fabric-2" , "fabric-3" ]},
356+ [_nebius_project ()],
357+ None ,
358+ id = "with-valid-fabrics" ,
359+ ),
360+ pytest .param (
361+ {"fabrics" : ["fabric-2" , "fabric-invalid" ]},
362+ [_nebius_project ()],
363+ "InfiniBand fabrics do not exist" ,
364+ id = "with-invalid-fabrics" ,
365+ ),
330366 ],
331367 )
332368 async def test_create (
333369 self ,
334370 test_db ,
335371 session : AsyncSession ,
336372 client : AsyncClient ,
337- config_regions : Optional [list [str ]],
338- config_projects : Optional [list [str ]],
373+ config_extra : dict [str , Any ],
339374 mocked_projects : Sequence [Any ],
340375 error : Optional [str ],
341376 ):
@@ -347,8 +382,7 @@ async def test_create(
347382 body = {
348383 "type" : "nebius" ,
349384 "creds" : FAKE_NEBIUS_SERVICE_ACCOUNT_CREDS ,
350- "regions" : config_regions ,
351- "projects" : config_projects ,
385+ ** config_extra ,
352386 }
353387 with patch (
354388 "dstack._internal.core.backends.nebius.resources.list_tenant_projects"
0 commit comments