Skip to content

Commit e411cc3

Browse files
committed
Fix tests
1 parent b87b240 commit e411cc3

6 files changed

Lines changed: 168 additions & 62 deletions

File tree

tests/auth/test_auth.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def test_init_with_env_variables(self, mock_environ, mock_tapis):
2323
base_url="https://designsafe.tapis.io",
2424
username="test_user",
2525
password="test_password",
26+
download_latest_specs=False,
2627
)
2728
mock_tapis_obj.get_tokens.assert_called_once()
2829
self.assertEqual(result, mock_tapis_obj)
@@ -49,6 +50,7 @@ def test_init_with_user_input(
4950
base_url="https://designsafe.tapis.io",
5051
username="test_user",
5152
password="test_password",
53+
download_latest_specs=False,
5254
)
5355
mock_tapis_obj.get_tokens.assert_called_once()
5456
self.assertEqual(result, mock_tapis_obj)

tests/jobs/test_archive_config.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import unittest
2-
from unittest.mock import MagicMock, patch
2+
from unittest.mock import MagicMock, Mock, patch
33
from dapi.jobs import generate_job_request
44

55

6+
def _make_app_arg(name, arg="", input_mode="INCLUDE_ON_DEMAND"):
7+
"""Create a mock app arg with a real .name attribute."""
8+
m = Mock()
9+
m.name = name
10+
m.arg = arg
11+
m.inputMode = input_mode
12+
return m
13+
14+
615
class TestArchiveConfiguration(unittest.TestCase):
716
"""Test cases for archive system configuration in job generation"""
817

@@ -30,13 +39,20 @@ def setUp(self):
3039
self.mock_job_attrs.memoryMB = 1000
3140
self.mock_job_attrs.isMpi = False
3241

33-
# Mock parameter set
34-
self.mock_param_set = MagicMock()
35-
self.mock_param_set.appArgs = [MagicMock(name="Main Script")]
42+
# Mock parameter set with properly named appArgs
43+
self.mock_param_set = Mock()
44+
self.mock_param_set.appArgs = [_make_app_arg("Main Script")]
3645
self.mock_param_set.envVariables = []
3746
self.mock_param_set.schedulerOptions = []
3847
self.mock_job_attrs.parameterSet = self.mock_param_set
3948

49+
# Mock fileInputs so input directory detection works
50+
input_dir_fi = Mock()
51+
input_dir_fi.name = "Input Directory"
52+
input_dir_fi.targetPath = None
53+
input_dir_fi.autoMountLocal = True
54+
self.mock_job_attrs.fileInputs = [input_dir_fi]
55+
4056
self.mock_app.jobAttributes = self.mock_job_attrs
4157

4258
@patch("dapi.jobs.get_app_details")

tests/jobs/test_dir_uri.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import unittest
2-
from unittest.mock import MagicMock
2+
from unittest.mock import MagicMock, Mock
33
from dapi.files import get_ds_path_uri
4-
from tapipy.tapis import Tapis
54

65

76
class TestGetDsPathUri(unittest.TestCase):
87
def setUp(self):
9-
# Mocking the Tapis object
10-
self.t = MagicMock(spec=Tapis)
8+
# Use MagicMock without spec so dynamic attributes like .systems work
9+
self.t = MagicMock()
1110
self.t.username = "testuser"
1211

13-
# Correctly mocking the get method
14-
self.t.get = MagicMock()
15-
self.t.get.return_value.json.return_value = {"baseProject": {"uuid": "12345"}}
12+
# Mock the systems.getSystems call for project path lookups
13+
# Return a single matching system for any project query
14+
mock_system = Mock()
15+
mock_system.id = "project-12345"
16+
mock_system.description = "ProjA ProjB project"
17+
self.t.systems.getSystems.return_value = [mock_system]
1618

1719
def test_directory_patterns(self):
1820
test_cases = [

tests/jobs/test_job_gen_jobinfo.py

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,86 @@
44
from 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+
734
class 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(

tests/jobs/test_job_status.py

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,46 @@
11
import unittest
2-
from unittest.mock import Mock, patch
2+
from unittest.mock import Mock, patch, MagicMock
33
import dapi as ds
44

55

66
class TestGetStatus(unittest.TestCase):
7-
@patch("time.sleep", Mock()) # Mocks the sleep function
8-
def test_get_status(self):
9-
# Mock the Tapis client object
7+
@patch("dapi.jobs.SubmittedJob")
8+
def test_get_status(self, mock_submitted_job_cls):
109
mock_tapis = Mock()
1110

12-
# Define behavior for getJobStatus method
13-
mock_tapis.jobs.getJobStatus.side_effect = [
14-
Mock(status="PENDING"),
15-
Mock(status="PENDING"),
16-
Mock(status="RUNNING"),
17-
Mock(status="RUNNING"),
18-
Mock(status="FINISHED"),
19-
]
11+
# Set up the mock SubmittedJob instance
12+
mock_job_instance = MagicMock()
13+
mock_job_instance.get_status.return_value = "FINISHED"
14+
mock_submitted_job_cls.return_value = mock_job_instance
2015

21-
# Define behavior for getJob method
22-
mock_tapis.jobs.getJob.return_value = Mock(maxMinutes=1)
23-
24-
# Call get_status
25-
status = ds.jobs.get_status(mock_tapis, "some_job_uuid", tlapse=1)
16+
# Call get_job_status (no tlapse parameter)
17+
status = ds.jobs.get_job_status(mock_tapis, "some_job_uuid")
2618

2719
# Assert that the final status is "FINISHED"
2820
self.assertEqual(status, "FINISHED")
2921

30-
# Assert the methods were called the expected number of times
31-
mock_tapis.jobs.getJobStatus.assert_called_with(jobUuid="some_job_uuid")
32-
self.assertEqual(mock_tapis.jobs.getJobStatus.call_count, 5)
33-
mock_tapis.jobs.getJob.assert_called_once_with(jobUuid="some_job_uuid")
22+
# Assert SubmittedJob was created with the right arguments
23+
mock_submitted_job_cls.assert_called_once_with(mock_tapis, "some_job_uuid")
24+
mock_job_instance.get_status.assert_called_once_with(force_refresh=True)
3425

35-
@patch("time.sleep", Mock())
36-
def test_get_status_timeout(self):
37-
# Mock the Tapis client object
26+
@patch("dapi.jobs.SubmittedJob")
27+
def test_get_status_running(self, mock_submitted_job_cls):
3828
mock_tapis = Mock()
3929

40-
# Define behavior for getJobStatus method to simulate a job that doesn't finish
41-
mock_tapis.jobs.getJobStatus.return_value = Mock(status="RUNNING")
42-
43-
# Define behavior for getJob method
44-
mock_tapis.jobs.getJob.return_value = Mock(maxMinutes=1)
30+
# Set up the mock SubmittedJob instance to return RUNNING
31+
mock_job_instance = MagicMock()
32+
mock_job_instance.get_status.return_value = "RUNNING"
33+
mock_submitted_job_cls.return_value = mock_job_instance
4534

46-
# Call get_status
47-
status = ds.jobs.get_status(mock_tapis, "some_job_uuid", tlapse=1)
35+
# Call get_job_status
36+
status = ds.jobs.get_job_status(mock_tapis, "some_job_uuid")
4837

49-
# Assert that the final status is still "RUNNING" due to timeout
38+
# Assert that the status is "RUNNING"
5039
self.assertEqual(status, "RUNNING")
5140

52-
# Assert the methods were called the expected number of times
53-
expected_calls = 60 # 1 minute = 60 seconds, with tlapse=1
54-
self.assertGreaterEqual(mock_tapis.jobs.getJobStatus.call_count, expected_calls)
55-
mock_tapis.jobs.getJob.assert_called_once_with(jobUuid="some_job_uuid")
41+
# Assert the SubmittedJob was created correctly
42+
mock_submitted_job_cls.assert_called_once_with(mock_tapis, "some_job_uuid")
43+
mock_job_instance.get_status.assert_called_once_with(force_refresh=True)
5644

5745

5846
if __name__ == "__main__":

tests/jobs/test_runtime_summary.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import unittest
2-
from unittest.mock import Mock
2+
from unittest.mock import Mock, patch
33
from io import StringIO
44
import sys
55
from datetime import datetime, timedelta
66

77
import dapi as ds
8+
from dapi.jobs import SubmittedJob
89

910

1011
class TestRuntimeSummary(unittest.TestCase):
@@ -95,7 +96,18 @@ def capture_output(self, t_mock, job_id, verbose):
9596
try:
9697
out = StringIO()
9798
sys.stdout = out
98-
ds.jobs.runtime_summary(t_mock, job_id, verbose)
99+
# Patch SubmittedJob.__init__ to skip the isinstance check
100+
with patch.object(
101+
SubmittedJob,
102+
"__init__",
103+
lambda self, tc, ju: (
104+
setattr(self, "_tapis", tc)
105+
or setattr(self, "uuid", ju)
106+
or setattr(self, "_last_status", None)
107+
or setattr(self, "_job_details", None)
108+
),
109+
):
110+
ds.jobs.get_runtime_summary(t_mock, job_id, verbose)
99111
return out.getvalue().strip()
100112
finally:
101113
sys.stdout = saved_stdout

0 commit comments

Comments
 (0)