-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from narenaryan/feat/4
test(ISSUE-4): Improve code coverage
- Loading branch information
Showing
11 changed files
with
314 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[run] | ||
source = whispr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,3 +29,4 @@ lib64/ | |
.env | ||
*.creds | ||
.coverage* | ||
!.coveragerc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import os | ||
|
||
import yaml | ||
|
||
from whispr.logging import logger | ||
|
||
def write_to_yaml_file(config: dict, file_path: str): | ||
"""Writes a given config object to a file in YAML format""" | ||
if not os.path.exists(file_path): | ||
with open(file_path, "w", encoding="utf-8") as file: | ||
yaml.dump(config, file) | ||
logger.info(f"{file_path} has been created.") | ||
|
||
def load_config(file_path: str) -> dict: | ||
"""Loads a given config file""" | ||
try: | ||
with open(file_path, "r", encoding="utf-8") as file: | ||
return yaml.safe_load(file) | ||
except Exception as e: | ||
raise e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import os | ||
import subprocess | ||
import shlex | ||
|
||
from whispr.logging import logger | ||
|
||
def execute_command(command: tuple, no_env: bool, secrets: dict): | ||
"""Executes a Unix/Windows command. | ||
Arg: `no_env` decides whether secrets are passed vai environment or K:V pairs in command arguments. | ||
""" | ||
if not secrets: | ||
secrets = {} | ||
|
||
try: | ||
usr_command = shlex.split(command[0]) | ||
|
||
if no_env: | ||
# Pass as --env K=V format (secure) | ||
usr_command.extend([ | ||
f"{k}={v}" for k,v in secrets.items() | ||
]) | ||
else: | ||
# Pass via environment (slightly insecure) | ||
os.environ.update(secrets) | ||
|
||
sp = subprocess.run(usr_command, env=os.environ, shell=False, check=True) | ||
except subprocess.CalledProcessError as e: | ||
logger.error( | ||
f"Encountered a problem while running command: '{command[0]}'. Aborting." | ||
) | ||
raise e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
|
||
import os | ||
import yaml | ||
import unittest | ||
|
||
from unittest.mock import MagicMock, patch, mock_open | ||
from whispr.utils.io import write_to_yaml_file, load_config | ||
|
||
class IOUtilsTestCase(unittest.TestCase): | ||
"""Unit tests for the file utilities: write_to_yaml_file and load_config.""" | ||
|
||
def setUp(self): | ||
"""Set up mocks for logger and os.path methods.""" | ||
self.mock_logger = MagicMock() | ||
self.config = {"key": "value"} | ||
self.file_path = "test_config.yaml" | ||
|
||
@patch("whispr.utils.io.logger", new_callable=lambda: MagicMock()) | ||
@patch("builtins.open", new_callable=mock_open) | ||
@patch("os.path.exists", return_value=False) | ||
def test_write_to_yaml_file_creates_file(self, mock_exists, mock_open_file, mock_logger): | ||
"""Test that write_to_yaml_file creates a new file and writes config data as YAML.""" | ||
write_to_yaml_file(self.config, self.file_path) | ||
|
||
mock_open_file.assert_called_once_with(self.file_path, "w", encoding="utf-8") | ||
mock_open_file().write.assert_called() # Ensures that some content was written | ||
mock_logger.info.assert_called_once_with(f"{self.file_path} has been created.") | ||
|
||
@patch("whispr.utils.io.logger", new_callable=lambda: MagicMock()) | ||
@patch("builtins.open", new_callable=mock_open) | ||
@patch("os.path.exists", return_value=True) | ||
def test_write_to_yaml_file_does_not_overwrite_existing_file(self, mock_exists, mock_open_file, mock_logger): | ||
"""Test that write_to_yaml_file does not overwrite an existing file.""" | ||
write_to_yaml_file(self.config, self.file_path) | ||
|
||
mock_open_file.assert_not_called() | ||
mock_logger.info.assert_not_called() | ||
|
||
@patch("builtins.open", new_callable=mock_open, read_data="key: value") | ||
def test_load_config_success(self, mock_open_file): | ||
"""Test that load_config loads a YAML file and returns a config dictionary.""" | ||
result = load_config(self.file_path) | ||
|
||
mock_open_file.assert_called_once_with(self.file_path, "r", encoding="utf-8") | ||
self.assertEqual(result, {"key": "value"}) | ||
|
||
@patch("builtins.open", new_callable=mock_open) | ||
def test_load_config_file_not_found(self, mock_open_file): | ||
"""Test load_config raises an error if the file does not exist.""" | ||
mock_open_file.side_effect = FileNotFoundError | ||
|
||
with self.assertRaises(FileNotFoundError): | ||
load_config("non_existent.yaml") | ||
|
||
@patch("builtins.open", new_callable=mock_open) | ||
def test_load_config_yaml_error(self, mock_open_file): | ||
"""Test load_config raises an error for an invalid YAML file.""" | ||
mock_open_file.side_effect = yaml.YAMLError | ||
|
||
with self.assertRaises(yaml.YAMLError): | ||
load_config(self.file_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import unittest | ||
from unittest.mock import patch, MagicMock | ||
import subprocess | ||
import os | ||
|
||
from whispr.utils.process import execute_command | ||
|
||
|
||
class ProcessUtilsTestCase(unittest.TestCase): | ||
"""Unit tests for the execute_command function, which executes commands with optional secrets.""" | ||
|
||
def setUp(self): | ||
"""Set up test data and mocks for logger and os environment.""" | ||
self.command = ("echo Hello",) | ||
self.secrets = {"API_KEY": "123456"} | ||
self.no_env = True | ||
self.mock_logger = MagicMock() | ||
|
||
@patch("whispr.utils.process.logger", new_callable=lambda: MagicMock()) | ||
@patch("subprocess.run") | ||
def test_execute_command_with_no_env(self, mock_subprocess_run, mock_logger): | ||
"""Test execute_command with `no_env=True`, passing secrets as command arguments.""" | ||
execute_command(self.command, self.no_env, self.secrets) | ||
|
||
expected_command = ["echo", "Hello", "API_KEY=123456"] | ||
mock_subprocess_run.assert_called_once_with(expected_command, env=os.environ, shell=False, check=True) | ||
|
||
@patch("whispr.utils.process.logger", new_callable=lambda: MagicMock()) | ||
@patch("subprocess.run") | ||
@patch("os.environ.update") | ||
def test_execute_command_with_env(self, mock_env_update, mock_subprocess_run, mock_logger): | ||
"""Test execute_command with `no_env=False`, passing secrets via environment variables.""" | ||
execute_command(self.command, no_env=False, secrets=self.secrets) | ||
|
||
mock_env_update.assert_called_once_with(self.secrets) | ||
expected_command = ["echo", "Hello"] | ||
mock_subprocess_run.assert_called_once_with(expected_command, env=os.environ, shell=False, check=True) | ||
|
||
@patch("whispr.utils.process.logger", new_callable=lambda: MagicMock()) | ||
@patch("subprocess.run", side_effect=subprocess.CalledProcessError(1, "test")) | ||
def test_execute_command_called_process_error(self, mock_subprocess_run, mock_logger): | ||
"""Test execute_command handles CalledProcessError and logs an error message.""" | ||
with self.assertRaises(subprocess.CalledProcessError): | ||
execute_command(self.command, no_env=True, secrets=self.secrets) | ||
|
||
mock_logger.error.assert_called_once_with( | ||
f"Encountered a problem while running command: '{self.command[0]}'. Aborting." | ||
) | ||
|
||
@patch("whispr.utils.process.logger", new_callable=lambda: MagicMock()) | ||
@patch("subprocess.run") | ||
def test_execute_command_without_secrets(self, mock_subprocess_run, mock_logger): | ||
"""Test execute_command without any secrets.""" | ||
execute_command(self.command, no_env=True, secrets={}) | ||
|
||
expected_command = ["echo", "Hello"] | ||
mock_subprocess_run.assert_called_once_with(expected_command, env=os.environ, shell=False, check=True) | ||
mock_logger.error.assert_not_called() |
Oops, something went wrong.