Skip to content

Commit

Permalink
Merge pull request #45 from TeskaLabs/Enhancement/Load-json-var-from-…
Browse files Browse the repository at this point in the history
…file

Load variables from file.
  • Loading branch information
mithunbharadwaj authored Nov 20, 2023
2 parents 30018b7 + 65fd822 commit 4e20487
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
40 changes: 40 additions & 0 deletions asabiris/formatter/jinja/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import configparser

import asab
import pathlib
import json
import jinja2

from ...exceptions import PathError
Expand All @@ -26,6 +28,44 @@ def __init__(self, app, service_name="JinjaService"):
self.Variables = {}

self.Environment = jinja2.Environment()
self._load_variables_from_json()

def _load_variables_from_json(self):
"""
Load variables from a JSON file specified in the 'jinja' section of the configuration.
This function attempts to read the path to a JSON file from the 'variables' key
under the 'jinja' section of the configuration. If the 'jinja' section or the
'variables' key is missing, the function returns without loading any variables.
If the specified JSON file is found, it reads and updates the internal variables
with the contents of the file. If the file is not found, cannot be read, or contains
invalid JSON, a warning is logged and the function returns without updating variables.
Overwrites any existing variables from the configuration file with the same keys.
"""
try:
json_path_str = asab.Config.get('jinja', 'variables')
except (configparser.NoSectionError, configparser.NoOptionError):
return

json_path = pathlib.Path(json_path_str)
if not json_path.is_file():
L.warning("JSON file specified '{}' in configuration does not exist.".format(json_path))
return

try:
with open(json_path, 'r') as json_file:
json_data = json.load(json_file)
except IOError as io_err:
L.warning("Failed to read JSON file '{}': {}".format(json_path, io_err))
return
except json.JSONDecodeError as json_err:
L.warning("Invalid JSON format in file '{}': {}".format(json_path, json_err))
return

self.Variables.update(json_data)
L.debug("Variables successfully loaded from JSON file '{}'.".format(json_path))


async def format(self, template_path, template_params):
Expand Down
123 changes: 123 additions & 0 deletions test/test_loading_variables_from_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import unittest
from unittest.mock import patch, MagicMock, mock_open
from asabiris.formatter.jinja.service import JinjaFormatterService


class TestJinjaFormatterService(unittest.TestCase):
"""
Unit tests for the JinjaFormatterService class method _load_variables_from_json.
"""

@patch('builtins.open', new_callable=mock_open, read_data='{"key": "value"}')
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_successful_json_load(self, mock_config_get, mock_is_file, _):
"""
Test loading variables from a valid JSON file.
Ensures that variables are correctly loaded and updated.
"""
mock_config_get.return_value = 'valid.json'
mock_is_file.return_value = True
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service._load_variables_from_json()
self.assertEqual(service.Variables, {"key": "value"})

@patch('builtins.open', new_callable=mock_open, read_data='Not a JSON')
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_invalid_json_format(self, mock_config_get, mock_is_file, _):
"""
Test behavior when the JSON file is not properly formatted.
Ensures that the method handles invalid JSON gracefully.
"""
mock_config_get.return_value = 'invalid.json'
mock_is_file.return_value = True
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service._load_variables_from_json()
self.assertEqual(service.Variables, {})

@patch('builtins.open', mock_open(), create=True)
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_io_error_on_file_open(self, mock_config_get, mock_is_file):
"""
Test the handling of IOError when opening the JSON file.
Ensures that the method handles file opening errors correctly.
"""
mock_config_get.return_value = 'error.json'
mock_is_file.return_value = True
mock_open.side_effect = IOError("Failed to open file")
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service._load_variables_from_json()
self.assertEqual(service.Variables, {})

@patch('builtins.open', new_callable=mock_open, read_data='{"key": "new_value"}')
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_update_existing_variables(self, mock_config_get, mock_is_file, _):
"""
Test updating existing variables with new values from the JSON file.
Ensures that existing variables are correctly updated.
"""
mock_config_get.return_value = 'update.json'
mock_is_file.return_value = True
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service.Variables = {"key": "old_value"}
service._load_variables_from_json()
self.assertEqual(service.Variables, {"key": "new_value"})

@patch('builtins.open', new_callable=mock_open, read_data='{"new_key": "value"}')
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_add_new_variables(self, mock_config_get, mock_is_file, _):
"""
Test adding new variables from the JSON file to existing ones.
Ensures that new variables are correctly added.
"""
mock_config_get.return_value = 'add.json'
mock_is_file.return_value = True
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service.Variables = {"existing_key": "existing_value"}
service._load_variables_from_json()
self.assertEqual(service.Variables, {"existing_key": "existing_value", "new_key": "value"})

@patch('builtins.open', new_callable=mock_open, read_data='{}')
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_empty_json_file(self, mock_config_get, mock_is_file, _):
"""
Test behavior when the JSON file is empty.
Ensures that the method handles empty JSON files correctly.
"""
mock_config_get.return_value = 'empty.json'
mock_is_file.return_value = True
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service._load_variables_from_json()
self.assertEqual(service.Variables, {})


@patch('builtins.open', new_callable=mock_open, read_data='{"complex": {"nested": {"key": ["value1", {"subkey": "subvalue"}]}}}')
@patch('pathlib.Path.is_file')
@patch('asabiris.formatter.jinja.service.asab.Config.get')
def test_complex_nested_json_structure(self, mock_config_get, mock_is_file, _):
"""
Test loading variables from a JSON file with a complex nested structure.
This test is designed to challenge the method's ability to handle complex JSON structures.
"""
mock_config_get.return_value = 'complex.json'
mock_is_file.return_value = True
mock_app = MagicMock()
service = JinjaFormatterService(mock_app)
service._load_variables_from_json()
expected_structure = {"complex": {"nested": {"key": ["value1", {"subkey": "subvalue"}]}}}
self.assertEqual(service.Variables, expected_structure)


if __name__ == '__main__':
unittest.main()

0 comments on commit 4e20487

Please sign in to comment.