@@ -59,6 +59,42 @@ def test_init(self):
5959 assert executor .nemo_run_dir == "/workspace/nemo_run"
6060 assert executor .mounts == [{"path" : "/workspace" , "mount_path" : "/workspace" }]
6161
62+ def test_init_with_node_reservation (self ):
63+ """Test initialization with node_reservation parameter."""
64+ executor = LeptonExecutor (
65+ resource_shape = "gpu.8xh100-80gb" ,
66+ node_group = "my-node-group" ,
67+ container_image = "test-image" ,
68+ nodes = 2 ,
69+ gpus_per_node = 8 ,
70+ nemo_run_dir = "/workspace/nemo_run" ,
71+ mounts = [{"path" : "/workspace" , "mount_path" : "/workspace" }],
72+ node_reservation = "my-reservation-id" ,
73+ )
74+
75+ assert executor .node_reservation == "my-reservation-id"
76+
77+ def test_init_with_empty_node_reservation (self ):
78+ """Test initialization with empty node_reservation string."""
79+ executor = LeptonExecutor (
80+ container_image = "test-image" ,
81+ nemo_run_dir = "/test/path" ,
82+ mounts = [{"path" : "/test" , "mount_path" : "/test" }],
83+ node_reservation = "" ,
84+ )
85+
86+ assert executor .node_reservation == ""
87+
88+ def test_init_without_node_reservation (self ):
89+ """Test initialization without node_reservation parameter (default behavior)."""
90+ executor = LeptonExecutor (
91+ container_image = "test-image" ,
92+ nemo_run_dir = "/test/path" ,
93+ mounts = [{"path" : "/test" , "mount_path" : "/test" }],
94+ )
95+
96+ assert executor .node_reservation == ""
97+
6298 @patch ("nemo_run.core.execution.lepton.APIClient" )
6399 def test_stop_job (self , mock_APIClient ):
64100 mock_instance = MagicMock ()
@@ -344,6 +380,88 @@ def test_create_lepton_job(self, mock_APIClient_class):
344380
345381 mock_client .job .create .assert_called_once ()
346382
383+ @patch ("nemo_run.core.execution.lepton.APIClient" )
384+ def test_create_lepton_job_with_reservation_config (self , mock_APIClient_class ):
385+ """Test create_lepton_job creates ReservationConfig when node_reservation is set."""
386+ mock_client = mock_APIClient_class .return_value
387+ mock_client .job .create .return_value = LeptonJob (metadata = Metadata (id = "my-lepton-job" ))
388+ node_group = SimpleNamespace (metadata = SimpleNamespace (id_ = "123456" ))
389+
390+ mock_client .nodegroup .list_all .return_value = []
391+ valid_node_ids = ["node-id-1" , "node-id-2" ]
392+
393+ executor = LeptonExecutor (
394+ container_image = "test-image" ,
395+ nemo_run_dir = "/test/path" ,
396+ node_group = "123456" ,
397+ mounts = [{"path" : "/test" , "mount_path" : "/test" }],
398+ node_reservation = "my-reservation-id" ,
399+ )
400+ executor ._valid_node_ids = MagicMock (return_value = valid_node_ids )
401+ executor ._node_group_id = MagicMock (return_value = node_group )
402+
403+ executor .create_lepton_job ("my-lepton-job" )
404+
405+ # Verify that job.create was called with the correct ReservationConfig
406+ mock_client .job .create .assert_called_once ()
407+ created_job = mock_client .job .create .call_args [0 ][0 ]
408+ assert created_job .spec .reservation_config is not None
409+ assert created_job .spec .reservation_config .reservation_id == "my-reservation-id"
410+
411+ @patch ("nemo_run.core.execution.lepton.APIClient" )
412+ def test_create_lepton_job_without_reservation_config (self , mock_APIClient_class ):
413+ """Test create_lepton_job creates no ReservationConfig when node_reservation is not set."""
414+ mock_client = mock_APIClient_class .return_value
415+ mock_client .job .create .return_value = LeptonJob (metadata = Metadata (id = "my-lepton-job" ))
416+ node_group = SimpleNamespace (metadata = SimpleNamespace (id_ = "123456" ))
417+
418+ mock_client .nodegroup .list_all .return_value = []
419+ valid_node_ids = ["node-id-1" , "node-id-2" ]
420+
421+ executor = LeptonExecutor (
422+ container_image = "test-image" ,
423+ nemo_run_dir = "/test/path" ,
424+ node_group = "123456" ,
425+ mounts = [{"path" : "/test" , "mount_path" : "/test" }],
426+ # No node_reservation set
427+ )
428+ executor ._valid_node_ids = MagicMock (return_value = valid_node_ids )
429+ executor ._node_group_id = MagicMock (return_value = node_group )
430+
431+ executor .create_lepton_job ("my-lepton-job" )
432+
433+ # Verify that job.create was called with no ReservationConfig
434+ mock_client .job .create .assert_called_once ()
435+ created_job = mock_client .job .create .call_args [0 ][0 ]
436+ assert created_job .spec .reservation_config is None
437+
438+ @patch ("nemo_run.core.execution.lepton.APIClient" )
439+ def test_create_lepton_job_with_empty_reservation_config (self , mock_APIClient_class ):
440+ """Test create_lepton_job creates no ReservationConfig when node_reservation is empty string."""
441+ mock_client = mock_APIClient_class .return_value
442+ mock_client .job .create .return_value = LeptonJob (metadata = Metadata (id = "my-lepton-job" ))
443+ node_group = SimpleNamespace (metadata = SimpleNamespace (id_ = "123456" ))
444+
445+ mock_client .nodegroup .list_all .return_value = []
446+ valid_node_ids = ["node-id-1" , "node-id-2" ]
447+
448+ executor = LeptonExecutor (
449+ container_image = "test-image" ,
450+ nemo_run_dir = "/test/path" ,
451+ node_group = "123456" ,
452+ mounts = [{"path" : "/test" , "mount_path" : "/test" }],
453+ node_reservation = "" , # Empty string
454+ )
455+ executor ._valid_node_ids = MagicMock (return_value = valid_node_ids )
456+ executor ._node_group_id = MagicMock (return_value = node_group )
457+
458+ executor .create_lepton_job ("my-lepton-job" )
459+
460+ # Verify that job.create was called with no ReservationConfig
461+ mock_client .job .create .assert_called_once ()
462+ created_job = mock_client .job .create .call_args [0 ][0 ]
463+ assert created_job .spec .reservation_config is None
464+
347465 def test_nnodes (self ):
348466 executor = LeptonExecutor (
349467 container_image = "nvcr.io/nvidia/test:latest" ,
0 commit comments