From dc30d853917932da20091914d406ff0df76c7537 Mon Sep 17 00:00:00 2001 From: tejasrok007 Date: Wed, 9 Oct 2024 10:36:48 +0530 Subject: [PATCH] Fix #38 implement unit test for config --- Config/check_config_validity.py | 6 +- Config/config.py | 2 +- Config/test_config.py | 69 ++++++++++++++++++++++ Config/test_formatter.py | 44 ++++++++++++++ Tests/Config/test_check_config_validity.py | 22 +++++++ Tests/Config/test_config.py | 28 ++++++++- 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 Config/test_config.py create mode 100644 Config/test_formatter.py create mode 100644 Tests/Config/test_check_config_validity.py diff --git a/Config/check_config_validity.py b/Config/check_config_validity.py index 9b12e6d..e9f6a4e 100644 --- a/Config/check_config_validity.py +++ b/Config/check_config_validity.py @@ -1,7 +1,8 @@ import configparser config = configparser.ConfigParser() -config.read("Config/logger.ini") +def read_config(): + return config.read('Config/logger.ini') # check if the required sections are present if "loggers" not in config.sections() or "handlers" not in config.sections(): @@ -20,3 +21,6 @@ if not isinstance(config.getint("loggers", "keys.suspicious.level"), int): raise ValueError("Invalid value for keys.suspicious.level in config.ini") + +if __name__ == "__main__": + read_config() \ No newline at end of file diff --git a/Config/config.py b/Config/config.py index cd28e5b..0fb643e 100644 --- a/Config/config.py +++ b/Config/config.py @@ -38,7 +38,7 @@ def get_logging_config(file_location="Config/logger.ini"): open(file_location, "r") except FileNotFoundError: logging.critical(f"File location invalid: {file_location}") - sys.exit(1) + raise FileNotFoundError config = get_config("Config/logger.ini") return config diff --git a/Config/test_config.py b/Config/test_config.py new file mode 100644 index 0000000..b6fd6cc --- /dev/null +++ b/Config/test_config.py @@ -0,0 +1,69 @@ +import unittest +from unittest.mock import patch, mock_open, MagicMock +import configparser +import os +import logging +from your_module import get_config, get_logging_config, configure_logging # type: ignore + +class TestConfigModule(unittest.TestCase): + + @patch("builtins.open", new_callable=mock_open, read_data="[ENVIRONMENT]\nVAR1=value1") + @patch("configparser.ConfigParser.read") + def test_get_config_file_parsed(self, mock_read, mock_open): + """ + Test if the configuration file is correctly parsed by get_config. + """ + config = get_config("fake_path") + mock_open.assert_called_with("fake_path", encoding="utf-8") + mock_read.assert_called_once() + + @patch("os.environ.get", side_effect=lambda key: "env_value" if key == "VAR1" else None) + @patch("configparser.ConfigParser.read", side_effect=lambda file, encoding: {"ENVIRONMENT": {"VAR1": "file_value"}}) + def test_get_config_env_overrides(self, mock_read, mock_environ): + """ + Test if environment variables override file values in get_config. + """ + config = get_config("fake_path") + self.assertEqual(config["ENVIRONMENT"]["VAR1"], "env_value") # env_value should override file_value + + @patch("builtins.open", mock_open()) + def test_get_logging_config_file_not_found(self): + """ + Test if FileNotFoundError is raised when logging config file is missing. + """ + with self.assertRaises(FileNotFoundError): + get_logging_config("invalid_path") + + @patch("os.path.exists", return_value=False) + @patch("os.makedirs") + @patch("builtins.open", new_callable=mock_open) + @patch("logging.config.fileConfig") + @patch("os.path.isdir", return_value=False) + def test_configure_logging_creates_files(self, mock_isdir, mock_fileconfig, mock_open, mock_makedirs, mock_exists): + """ + Test if configure_logging creates log files and directories when needed. + """ + configure_logging() + + mock_isdir.assert_called_with("Logs") + mock_makedirs.assert_called_once_with("Logs") + mock_open.assert_any_call("Logs/traceback.log", "w") + + @patch("logging.StreamHandler") + @patch("logging.FileHandler") + @patch("logging.Formatter") + @patch("os.makedirs") + @patch("builtins.open", new_callable=mock_open, read_data="[handler_file]\nargs=logfile.log\nmode=w\nencoding=utf-8") + @patch("configparser.ConfigParser.read", side_effect=lambda file, encoding: {"handler_file": {"args": "logfile.log", "mode": "w", "encoding": "utf-8"}}) + def test_configure_logging_handlers_set_up(self, mock_read, mock_open, mock_makedirs, mock_formatter, mock_filehandler, mock_streamhandler): + """ + Test if logging handlers (file and console) are correctly set up in configure_logging. + """ + logging_config = configure_logging() + + mock_streamhandler.assert_called_once() + mock_filehandler.assert_called_once_with("logfile.log", mode="w", encoding="utf-8") + mock_formatter.assert_called_once_with(fmt="%(asctime)s %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") + +if __name__ == "__main__": + unittest.main() diff --git a/Config/test_formatter.py b/Config/test_formatter.py new file mode 100644 index 0000000..6af3ead --- /dev/null +++ b/Config/test_formatter.py @@ -0,0 +1,44 @@ +import os +import sys +import unittest +import logging +sys.path.insert(0, os.getcwd()) +from Config.formatter import Formatter +from Config.config import configure_logging + + +class TestFormatter(unittest.TestCase): + def setUp(self): + # Ensure file paths are all set up + if not os.path.split(os.path.basename(os.getcwd()))[1] == "HackAlert": + print("Pathname invalid, ensure you're in the base directory.") + sys.exit(1) + if not os.path.isdir(os.path.join("logs")) or not os.path.isdir(os.path.join("logs", "tests")): + os.makedirs(os.path.join('logs', 'tests')) + files = [os.path.join("logs/tests/test_critical.log"), + os.path.join("logs/tests/test_error.log"), + os.path.join("logs/tests/test_warning.log"), + os.path.join("logs/tests/test_debug.log"), + os.path.join("logs/tests/test_info.log")] + for file_ in files: + if not os.path.exists(file_): + with open(file_, "w") as file: + file.write(f"Created {file_} for tests.") + file.close() + + configure_logging() + self.formatter = Formatter() + self.logger = logging.getLogger() + + def test_records(self): + self.records = { + "critical": self.logger.makeRecord( "test_critical", logging.CRITICAL, "logs/tests/test_critical.log", 50, "[test] critical message", (), None,), + "error": self.logger.makeRecord( "test_error", logging.ERROR, "logs/tests/test_error.log", 40, "[test] error message", (), None,), + "warning": self.logger.makeRecord( "test_warning", logging.WARNING, "logs/tests/test_warning.log", 30, "[test] warning message", (), None,), + "debug": self.logger.makeRecord( "test_debug", logging.DEBUG, "logs/tests/test_debug.log", 20, "[test] debug message", (), None,), + "info": self.logger.makeRecord( "test_info", logging.INFO, "logs/tests/test_info.log", 10, "[test] info message", (), None,), + } + + for record in self.records.keys(): + if self.assertIsInstance(self.records[record], logging.LogRecord) is not False: + self.assertIn(record, self.records[record].msg) \ No newline at end of file diff --git a/Tests/Config/test_check_config_validity.py b/Tests/Config/test_check_config_validity.py new file mode 100644 index 0000000..2a5aa80 --- /dev/null +++ b/Tests/Config/test_check_config_validity.py @@ -0,0 +1,22 @@ +import configparser +import unittest +import sys +import os +sys.path.insert(0, os.getcwd()) +from Config import check_config_validity + + +class TestConfigValid(unittest.TestCase): + def setUp(self): + check_config_validity.read_config() + self.config = configparser.ConfigParser() + + def test_key_value_format(self): + self.assertIsInstance(self.config.getint('handlers', 'console.level'), int) + self.assertIsInstance(self.config.getint('loggers', 'keys.suspicious.level'), int) + + def test_keys_present_in_handlers(self): + assert self.config['loggers'] or 'suspicious' in self.config ['loggers'] + + def test_sections_present(self): + assert 'loggers' in self.config.sections() or 'handlers' in self.config.sections() \ No newline at end of file diff --git a/Tests/Config/test_config.py b/Tests/Config/test_config.py index d241214..dd89c34 100644 --- a/Tests/Config/test_config.py +++ b/Tests/Config/test_config.py @@ -1,7 +1,13 @@ import os import unittest -from configparser import ConfigParser +import sys + +sys.path.insert(0, os.getcwd()) + +from requests.api import get # type: ignore +from Config.config import configure_logging, get_config, get_logging_config +from configparser import ConfigParser class TestConfig(unittest.TestCase): def setUp(self): @@ -28,8 +34,24 @@ def test_config_is_valid(self): # Check if the required keys are present in the sections self.assertIn("keys", parser["formatters"]) - self.assertIn("level", parser["handler_console"]) + self.assertIn('keys', parser['formatters']) + self.assertIn('level', parser['handler_console']) + + def test_directory_structure(self): + """ Check that config module is where it's supposed to be """ + + self.assertTrue(os.path.exists(os.path.join("Config", "config.py"))) + + def test_get_config(self): + self.assertIn("property", get_logging_config.__builtins__.keys()) + + def test_get_logging_config(self): + with self.assertRaises(FileNotFoundError) as _: + get_logging_config("random_path.txt") + self.assertIsInstance(get_logging_config(), ConfigParser) + def test_configure_logging(self): + self.assertIsInstance(configure_logging(), ConfigParser) if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file