Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unittests #31

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ def test_load_realm_class_module_not_found(self, mock_import_module):

@patch("importlib.import_module")
def test_load_realm_class_attribute_error(self, mock_import_module):
# Module exists but class does not
mock_module = MagicMock()
# Creating a mock module with no attributes allowed
# means any attribute access raises AttributeError.
mock_module = MagicMock(spec=[])

mock_import_module.return_value = mock_module

module_path = "some.module.MissingClass"
result = YggdrasilUtilities.load_realm_class(module_path)

self.assertIsNone(result)
mock_import_module.assert_called_with("some.module")

Expand Down Expand Up @@ -93,6 +94,22 @@ def test_load_module_import_error(self, mock_import_module):
self.assertIsNone(result)
mock_import_module.assert_called_with("nonexistent.module")

@patch("importlib.import_module")
def test_load_realm_class_caching(self, mock_import_module):
# First call: loads and caches the class
mock_module = MagicMock()
mock_import_module.return_value = mock_module
module_path = "some.module.ExistingClass"
first_result = YggdrasilUtilities.load_realm_class(module_path)
self.assertIsNotNone(first_result)

# Second call: should return the cached class without calling import_module again
second_result = YggdrasilUtilities.load_realm_class(module_path)
self.assertIs(first_result, second_result)
mock_import_module.assert_called_once_with(
"some.module"
) # Confirm only called once

def test_get_path_file_exists(self):
# Create a dummy config file
file_name = "config.yaml"
Expand Down Expand Up @@ -216,6 +233,16 @@ def test_get_path_with_absolute_file_name(self):
result = YggdrasilUtilities.get_path(file_name)
self.assertIsNone(result) # Should not allow absolute paths

@patch.object(YggdrasilUtilities, "CONFIG_DIR", Path("/some/base/path"))
def test_get_path_resolve_error(self):
# Mock the config_file.resolve() call to raise an Exception
# to trigger the exception block in get_path
with patch(
"lib.core_utils.common.Path.resolve", side_effect=Exception("Resolve error")
):
result = YggdrasilUtilities.get_path("somefile")
self.assertIsNone(result)


if __name__ == "__main__":
unittest.main()
23 changes: 13 additions & 10 deletions tests/test_config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ def test_config_immutable(self):
# Test that the configuration data is immutable
self.config_loader._config = types.MappingProxyType(self.mock_config_data)
with self.assertRaises(TypeError):
original_dict = self.mock_config_data
with self.assertRaises(TypeError):
original_dict["key1"] = "new_value"
self.config_loader._config["key1"] = "new_value" # type: ignore

def test_load_config_type_error(self):
# Test handling of TypeError during json.load
Expand Down Expand Up @@ -132,18 +130,23 @@ def test_config_manager_instance(self):
self.assertIsInstance(config_manager, ConfigLoader)

def test_configs_loaded(self):
# Test that configs are loaded when the module is imported
with patch("lib.core_utils.config_loader.Ygg.get_path") as mock_get_path, patch(
"builtins.open", mock_open(read_data=self.mock_config_json)
with patch(
"lib.core_utils.config_loader.config_manager.load_config",
return_value=types.MappingProxyType(self.mock_config_data),
):
mock_get_path.return_value = Path("/path/to/config.json")
# Reload the module to trigger the code at the module level
import sys

if "config_loader" in sys.modules:
del sys.modules["config_loader"]
if "lib.core_utils.config_loader" in sys.modules:
del sys.modules["lib.core_utils.config_loader"]

from lib.core_utils import config_loader

# Patch the configs directly
config_loader.configs = types.MappingProxyType(self.mock_config_data)

self.assertEqual(
config_loader.configs, types.MappingProxyType(self.mock_config_data)
)
self.assertEqual(
config_loader.configs, types.MappingProxyType(self.mock_config_data)
)
Expand Down
45 changes: 44 additions & 1 deletion tests/test_report_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,50 @@ def test_transfer_report_general_exception(self, mock_subprocess_run, mock_confi
mock_logging.error.assert_any_call(
"Unexpected error during report transfer: Unexpected error"
)
mock_logging.error.assert_any_call("RSYNC output: ")
mock_logging.error.assert_any_call(
"RSYNC output: No output available due to early error."
)

@patch("lib.module_utils.report_transfer.configs")
@patch("lib.module_utils.report_transfer.subprocess.run")
@patch("lib.module_utils.report_transfer.logging")
def test_transfer_report_general_exception_with_result(
self, mock_logging, mock_subprocess_run, mock_configs
):
# Set up a valid config
mock_configs.__getitem__.return_value = {
"server": self.server,
"user": self.user,
"destination": self.remote_dir_base,
"ssh_key": self.ssh_key,
}

# Mock a successful subprocess run
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = "Mocked RSYNC output"
mock_subprocess_run.return_value = mock_result

# Make logging.info raise an exception to simulate an error after success
def info_side_effect(*args, **kwargs):
raise Exception("Logging info error")

mock_logging.info.side_effect = info_side_effect

# Call the function
result = transfer_report(self.report_path, self.project_id, self.sample_id)

# Assert that the result is False because the exception should cause failure
self.assertFalse(result)

# Check that the unexpected error was logged
# The code logs: "Unexpected error during report transfer: Logging info error"
mock_logging.error.assert_any_call(
"Unexpected error during report transfer: Logging info error"
)

# Check that the RSYNC output was logged
mock_logging.error.assert_any_call("RSYNC output: Mocked RSYNC output")

@patch("lib.module_utils.report_transfer.configs")
@patch("lib.module_utils.report_transfer.subprocess.run")
Expand Down
4 changes: 2 additions & 2 deletions tests/test_slurm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,12 @@ def test_generate_slurm_script_template_syntax_error(self, mock_file, mock_path)
def test_generate_slurm_script_invalid_template_path_type(self):
# Test with invalid type for template_fpath
with self.assertRaises(TypeError):
generate_slurm_script(self.args_dict, None, self.output_fpath)
generate_slurm_script(self.args_dict, None, self.output_fpath) # type: ignore

def test_generate_slurm_script_invalid_output_path_type(self):
# Test with invalid type for output_fpath
with self.assertRaises(TypeError):
generate_slurm_script(self.args_dict, self.template_fpath, None)
generate_slurm_script(self.args_dict, self.template_fpath, None) # type: ignore

@patch("lib.module_utils.slurm_utils.Path")
@patch("builtins.open", new_callable=mock_open)
Expand Down
Loading