44from datetime import datetime
55
66
7+ def _make_app_arg (name , arg = "" , input_mode = "INCLUDE_ON_DEMAND" ):
8+ """Create a mock app arg with a real .name attribute."""
9+ m = Mock ()
10+ m .name = name
11+ m .arg = arg
12+ m .inputMode = input_mode
13+ return m
14+
15+
16+ def _make_env_var (key , value = "" , input_mode = "INCLUDE_ON_DEMAND" ):
17+ """Create a mock env var with a real .key attribute."""
18+ m = Mock ()
19+ m .key = key
20+ m .value = value
21+ m .inputMode = input_mode
22+ return m
23+
24+
25+ def _make_file_input (name , target_path = None , auto_mount = True ):
26+ """Create a mock file input definition."""
27+ m = Mock ()
28+ m .name = name
29+ m .targetPath = target_path
30+ m .autoMountLocal = auto_mount
31+ return m
32+
33+
734class TestGenerateJobInfo (unittest .TestCase ):
835 def setUp (self ):
936 self .t_mock = Mock ()
1037 self .app_name = "test-app"
1138 self .input_uri = "tapis://test-system/input/data"
1239 self .input_file = "input.txt"
13- # Mock the getAppLatestVersion method
40+
41+ # Mock app details (returned by get_app_details)
1442 self .app_info_mock = Mock ()
1543 self .app_info_mock .id = self .app_name
1644 self .app_info_mock .version = "1.0"
45+ self .app_info_mock .description = "Test app"
46+
47+ # Job attributes
1748 self .app_info_mock .jobAttributes .execSystemId = "test-exec-system"
1849 self .app_info_mock .jobAttributes .maxMinutes = 60
1950 self .app_info_mock .jobAttributes .archiveOnAppError = True
2051 self .app_info_mock .jobAttributes .execSystemLogicalQueue = "normal"
21- self .t_mock .apps .getAppLatestVersion .return_value = self .app_info_mock
52+ self .app_info_mock .jobAttributes .nodeCount = 1
53+ self .app_info_mock .jobAttributes .coresPerNode = 1
54+ self .app_info_mock .jobAttributes .memoryMB = None
55+ self .app_info_mock .jobAttributes .isMpi = None
56+ self .app_info_mock .jobAttributes .cmdPrefix = None
57+ self .app_info_mock .jobAttributes .archiveSystemId = None
58+ self .app_info_mock .jobAttributes .archiveSystemDir = None
59+
60+ # Parameter set with proper mock objects
61+ param_set = Mock ()
62+ param_set .appArgs = [_make_app_arg ("Input Script" )]
63+ param_set .envVariables = []
64+ param_set .schedulerOptions = []
65+ self .app_info_mock .jobAttributes .parameterSet = param_set
2266
67+ # File inputs
68+ self .app_info_mock .jobAttributes .fileInputs = [
69+ _make_file_input ("Input Directory" )
70+ ]
71+
72+ @patch ("dapi.jobs.get_app_details" )
2373 @patch ("dapi.jobs.datetime" )
24- def test_generate_job_info_default (self , mock_datetime ):
74+ def test_generate_job_info_default (self , mock_datetime , mock_get_app ):
2575 mock_datetime .now .return_value = datetime (2023 , 5 , 1 , 12 , 0 , 0 )
76+ mock_get_app .return_value = self .app_info_mock
77+
2678 result = generate_job_request (
2779 self .t_mock , self .app_name , self .input_uri , self .input_file
2880 )
29- self .assertEqual (result ["name" ], f"{ self .app_name } _20230501_120000 " )
81+ self .assertEqual (result ["name" ], f"{ self .app_name } -20230501_120000 " )
3082 self .assertEqual (result ["appId" ], self .app_name )
3183 self .assertEqual (result ["appVersion" ], "1.0" )
3284 self .assertEqual (result ["execSystemId" ], "test-exec-system" )
3385 self .assertEqual (result ["maxMinutes" ], 60 )
3486 self .assertTrue (result ["archiveOnAppError" ])
35- self .assertEqual (
36- result ["fileInputs" ],
37- [{"name" : "Input Directory" , "sourceUrl" : self .input_uri }],
38- )
3987 self .assertEqual (result ["execSystemLogicalQueue" ], "normal" )
4088 self .assertEqual (result ["nodeCount" ], 1 )
4189 self .assertEqual (result ["coresPerNode" ], 1 )
@@ -45,7 +93,10 @@ def test_generate_job_info_default(self, mock_datetime):
4593 )
4694 self .assertNotIn ("schedulerOptions" , result ["parameterSet" ])
4795
48- def test_generate_job_info_custom (self ):
96+ @patch ("dapi.jobs.get_app_details" )
97+ def test_generate_job_info_custom (self , mock_get_app ):
98+ mock_get_app .return_value = self .app_info_mock
99+
49100 custom_job_name = "custom-job"
50101 custom_max_minutes = 120
51102 custom_node_count = 2
@@ -73,14 +124,49 @@ def test_generate_job_info_custom(self):
73124 [{"name" : "TACC Allocation" , "arg" : f"-A { custom_allocation } " }],
74125 )
75126
76- def test_generate_job_info_invalid_app (self ):
77- self .t_mock .apps .getAppLatestVersion .side_effect = Exception ("Invalid app" )
127+ @patch ("dapi.jobs.get_app_details" )
128+ def test_generate_job_info_invalid_app (self , mock_get_app ):
129+ mock_get_app .side_effect = Exception ("Invalid app" )
78130 with self .assertRaises (Exception ):
79131 generate_job_request (self .t_mock , "invalid-app" , self .input_uri )
80132
81- def test_generate_job_info_opensees (self ):
133+ @patch ("dapi.jobs.get_app_details" )
134+ def test_generate_job_info_opensees (self , mock_get_app ):
82135 opensees_app_name = "opensees-express"
83- result = generate_job_request (self .t_mock , opensees_app_name , self .input_uri )
136+
137+ # Create a separate app mock for opensees with envVariables containing tclScript
138+ opensees_app = Mock ()
139+ opensees_app .id = opensees_app_name
140+ opensees_app .version = "1.0"
141+ opensees_app .description = "OpenSees app"
142+ opensees_app .jobAttributes .execSystemId = "test-exec-system"
143+ opensees_app .jobAttributes .maxMinutes = 60
144+ opensees_app .jobAttributes .archiveOnAppError = True
145+ opensees_app .jobAttributes .execSystemLogicalQueue = "normal"
146+ opensees_app .jobAttributes .nodeCount = 1
147+ opensees_app .jobAttributes .coresPerNode = 1
148+ opensees_app .jobAttributes .memoryMB = None
149+ opensees_app .jobAttributes .isMpi = None
150+ opensees_app .jobAttributes .cmdPrefix = None
151+ opensees_app .jobAttributes .archiveSystemId = None
152+ opensees_app .jobAttributes .archiveSystemDir = None
153+
154+ # OpenSees uses envVariables for the script, not appArgs
155+ param_set = Mock ()
156+ param_set .appArgs = []
157+ param_set .envVariables = [_make_env_var ("tclScript" )]
158+ param_set .schedulerOptions = []
159+ opensees_app .jobAttributes .parameterSet = param_set
160+ opensees_app .jobAttributes .fileInputs = [_make_file_input ("Input Directory" )]
161+
162+ mock_get_app .return_value = opensees_app
163+
164+ result = generate_job_request (
165+ self .t_mock ,
166+ opensees_app_name ,
167+ self .input_uri ,
168+ script_filename = self .input_file ,
169+ )
84170 self .assertIn ("parameterSet" , result )
85171 self .assertIn ("envVariables" , result ["parameterSet" ])
86172 self .assertEqual (
0 commit comments