diff --git a/.coveragerc b/.coveragerc index 88b761d..0072559 100644 --- a/.coveragerc +++ b/.coveragerc @@ -7,6 +7,7 @@ omit = */migrations/* */tests/* */testing/* + */configs/* */apps.py manage.py diff --git a/.rackcoveragerc b/.rackcoveragerc index 270f1d3..f154bfc 100644 --- a/.rackcoveragerc +++ b/.rackcoveragerc @@ -8,7 +8,7 @@ exclude = *.__* *.get_absolute_url net.xeaf.rack.core.testing.* - net.xeaf.rack.core.views.experimental.* + net.xeaf.rack.utils.settings.config.* # То, что мы ПРИНУДИТЕЛЬНО возвращаем назад (маски включения) # Этот список имеет наивысший приоритет! diff --git a/net/xeaf/rack/tests/core/__init__.py b/net/xeaf/rack/tests/core/__init__.py index 0e77f7f..43bfc3b 100644 --- a/net/xeaf/rack/tests/core/__init__.py +++ b/net/xeaf/rack/tests/core/__init__.py @@ -8,6 +8,10 @@ Пакет описания тестов классов ядра """ +from .core_command_clear_database_tests import CoreCommandClearDatabaseTests +from .core_command_is_debug_or_test_tests import CoreCommandIsDebugOrTestTests +from .core_command_report_int_value_tests import CoreCommandReportIntValueTests +from .core_command_report_separator_tests import CoreCommandReportSeparatorTests from .core_enum_check_is_empty_tests import CoreEnumCheckIsEmptyTests from .core_enum_choices_tests import CoreEnumChoicesTests from .core_enum_from_string_tests import CoreEnumFromStringTests diff --git a/net/xeaf/rack/tests/utils/__init__.py b/net/xeaf/rack/tests/utils/__init__.py new file mode 100644 index 0000000..5418725 --- /dev/null +++ b/net/xeaf/rack/tests/utils/__init__.py @@ -0,0 +1,11 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Пакет описания тестов для служебных классов +""" + +from .settings import * diff --git a/net/xeaf/rack/tests/utils/settings/__init__.py b/net/xeaf/rack/tests/utils/settings/__init__.py new file mode 100644 index 0000000..6f14a83 --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/__init__.py @@ -0,0 +1,20 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Пакет описания классов тестов классов работ с настройками +""" + +from .environment_check_initialization_tests import EnvironmentCheckInitializationTests +from .environment_env_name_tests import EnvironmentEnvNameTests +from .environment_env_tests import EnvironmentEnvTests +from .environment_get_bool_tests import EnvironmentGetBoolTests +from .environment_get_int_tests import EnvironmentGetIntTests +from .environment_get_str_as_dir_tests import EnvironmentGetStrAsDirTests +from .environment_get_str_as_file_tests import EnvironmentGetStrAsFileTests +from .environment_get_str_as_list_tests import EnvironmentGetStrAsListTests +from .environment_get_str_tests import EnvironmentGetStrTests +from .environment_initialize_tests import EnvironmentInitializeTests diff --git a/net/xeaf/rack/tests/utils/settings/environment_check_initialization_tests.py b/net/xeaf/rack/tests/utils/settings/environment_check_initialization_tests.py new file mode 100644 index 0000000..9e3fcfa --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_check_initialization_tests.py @@ -0,0 +1,52 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentCheckInitializationTests +""" + +from django.core.exceptions import ImproperlyConfigured + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + +class EnvironmentCheckInitializationTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment._check_initialization() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + + super().setUp() + Environment._is_initialized = False + + def test_check_initialization_raises_error_when_not_initialized(self): + """ + Тест проверяет выбрасывание ошибки при отсутствии инициализации + """ + + # Act & Assert + with self.assertRaises(ImproperlyConfigured) as context: + Environment._check_initialization() + + self.assertIn("not initialized", str(context.exception)) + + def test_check_initialization_does_not_raise_when_initialized(self): + """ + Тест проверяет отсутствие ошибки при инициализации + """ + + # Arrange + Environment._is_initialized = True + + # Act & Assert + try: + Environment._check_initialization() + except ImproperlyConfigured: + self.fail("_check_initialization raised ImproperlyConfigured unexpectedly") diff --git a/net/xeaf/rack/tests/utils/settings/environment_env_name_tests.py b/net/xeaf/rack/tests/utils/settings/environment_env_name_tests.py new file mode 100644 index 0000000..5bb1a49 --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_env_name_tests.py @@ -0,0 +1,82 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentEnvNameTests +""" + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentEnvNameTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment._env_name() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + + super().setUp() + Environment._is_initialized = True + + def test_env_name_adds_prefix(self): + """ + Тест проверяет добавление префикса к имени переменной + """ + + # Arrange + Environment._env_prefix = "PREFIX_" + + # Act + result = Environment._env_name("VAR_NAME") + + # Assert + self.assertEqual(result, "PREFIX_VAR_NAME") + + def test_env_name_with_empty_prefix(self): + """ + Тест проверяет обработку пустого префикса + """ + + # Arrange + Environment._env_prefix = "" + + # Act + result = Environment._env_name("VAR_NAME") + + # Assert + self.assertEqual(result, "VAR_NAME") + + def test_env_name_handles_empty_name(self): + """ + Тест проверяет обработку пустого имени переменной + """ + + # Arrange + Environment._env_prefix = "PREFIX_" + + # Act + result = Environment._env_name("") + + # Assert + self.assertEqual(result, "PREFIX_") + + def test_env_name_preserves_case(self): + """ + Тест проверяет сохранение регистра имени переменной + """ + + # Arrange + Environment._env_prefix = "PREFIX_" + + # Act + result = Environment._env_name("ApiKey") + + # Assert + self.assertEqual(result, "PREFIX_ApiKey") diff --git a/net/xeaf/rack/tests/utils/settings/environment_env_tests.py b/net/xeaf/rack/tests/utils/settings/environment_env_tests.py new file mode 100644 index 0000000..5b7734a --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_env_tests.py @@ -0,0 +1,55 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentEnvTests +""" + +from unittest.mock import Mock + +from django.core.exceptions import ImproperlyConfigured + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentEnvTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.env() + """ + + def setUp(self): + """ + Подготовка тестовой среды: сброс состояния Environment + """ + super().setUp() + Environment._is_initialized = True + + def test_env_returns_env_object_when_initialized(self): + """ + Тест проверяет, что env() возвращает объект Env при инициализации + """ + # Arrange + mock_env = Mock() + Environment._env = mock_env + + # Act + result = Environment.env() + + # Assert + self.assertEqual(result, mock_env) + + def test_env_raises_error_when_not_initialized(self): + """ + Тест проверяет, что env() выбрасывает ошибку при отсутствии инициализации + """ + # Arrange + Environment._is_initialized = False + + # Act & Assert + with self.assertRaises(ImproperlyConfigured) as context: + Environment.env() + self.assertIn("not initialized", str(context.exception)) diff --git a/net/xeaf/rack/tests/utils/settings/environment_get_bool_tests.py b/net/xeaf/rack/tests/utils/settings/environment_get_bool_tests.py new file mode 100644 index 0000000..def373f --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_get_bool_tests.py @@ -0,0 +1,68 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentGetBoolTests +""" + +from unittest.mock import Mock +from unittest.mock import patch + +from environ import Env + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentGetBoolTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.get_bool() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + + super().setUp() + Environment._is_initialized = True + Environment._env_prefix = "APP_" + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_bool_returns_boolean_value(self, mock_env): + """ + Тест проверяет получение логического значения переменной окружения + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.bool.return_value = True + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_bool("DEBUG") + + # Assert + self.assertTrue(result) + mock_env_instance.bool.assert_called_once_with("APP_DEBUG", Env.NOTSET) + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_bool_with_default_value(self, mock_env): + """ + Тест проверяет получение логического значения с указанием значения по умолчанию + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.bool.return_value = False + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_bool("DEBUG", False) + + # Assert + self.assertFalse(result) + mock_env_instance.bool.assert_called_once_with("APP_DEBUG", False) diff --git a/net/xeaf/rack/tests/utils/settings/environment_get_int_tests.py b/net/xeaf/rack/tests/utils/settings/environment_get_int_tests.py new file mode 100644 index 0000000..1cca598 --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_get_int_tests.py @@ -0,0 +1,83 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentGetIntTests +""" + +from unittest.mock import Mock +from unittest.mock import patch + +from environ import Env + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentGetIntTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.get_int() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + super().setUp() + Environment._is_initialized = True + Environment._env_prefix = "APP_" + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_int_returns_integer_value(self, mock_env): + """ + Тест проверяет получение целочисленного значения переменной окружения + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.int.return_value = 42 + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_int("PORT") + + # Assert + self.assertEqual(result, 42) + mock_env_instance.int.assert_called_once_with("APP_PORT", Env.NOTSET) + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_int_with_default_value(self, mock_env): + """ + Тест проверяет получение целочисленного значения с указанием значения по умолчанию + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.int.return_value = 8080 + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_int("PORT", 8080) + + # Assert + self.assertEqual(result, 8080) + mock_env_instance.int.assert_called_once_with("APP_PORT", 8080) + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_int_raises_error_for_invalid_value(self, mock_env): + """ + Тест проверяет выбрасывание ошибки при невалидном целочисленном значении + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.int.side_effect = ValueError("Invalid integer") + mock_env.return_value = mock_env_instance + + # Act & Assert + with self.assertRaises(ValueError) as context: + Environment.get_int("PORT") + self.assertIn("Invalid integer", str(context.exception)) diff --git a/net/xeaf/rack/tests/utils/settings/environment_get_str_as_dir_tests.py b/net/xeaf/rack/tests/utils/settings/environment_get_str_as_dir_tests.py new file mode 100644 index 0000000..dd32677 --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_get_str_as_dir_tests.py @@ -0,0 +1,79 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentGetStrAsDirTests +""" + +from unittest.mock import patch + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentGetStrAsDirTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.get_str_as_dir() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + super().setUp() + Environment._is_initialized = True + Environment._env_prefix = "APP_" + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + @patch('os.makedirs') + def test_get_str_as_dir_creates_directory(self, mock_makedirs, mock_get_str): + """ + Тест проверяет создание директории + """ + + # Arrange + mock_get_str.return_value = "/tmp/test_dir" + + # Act + result = Environment.get_str_as_dir("STORAGE_PATH") + + # Assert + self.assertEqual(result, "/tmp/test_dir") + mock_makedirs.assert_called_once_with("/tmp/test_dir", exist_ok=True) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + @patch('os.makedirs') + def test_get_str_as_dir_with_default_value(self, mock_makedirs, mock_get_str): + """ + Тест проверяет создание директории со значением по умолчанию + """ + + # Arrange + mock_get_str.return_value = "/default/path" + + # Act + result = Environment.get_str_as_dir("STORAGE_PATH", "/default/path") + + # Assert + self.assertEqual(result, "/default/path") + mock_makedirs.assert_called_once_with("/default/path", exist_ok=True) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + @patch('os.makedirs') + def test_get_str_as_dir_handles_relative_path(self, mock_makedirs, mock_get_str): + """ + Тест проверяет создание директории с относительным путем + """ + + # Arrange + mock_get_str.return_value = "media/uploads" + + # Act + result = Environment.get_str_as_dir("UPLOAD_DIR") + + # Assert + self.assertEqual(result, "media/uploads") + mock_makedirs.assert_called_once_with("media/uploads", exist_ok=True) diff --git a/net/xeaf/rack/tests/utils/settings/environment_get_str_as_file_tests.py b/net/xeaf/rack/tests/utils/settings/environment_get_str_as_file_tests.py new file mode 100644 index 0000000..b41373e --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_get_str_as_file_tests.py @@ -0,0 +1,90 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentGetStrAsFileTests +""" + +from unittest.mock import patch + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentGetStrAsFileTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.get_str_as_file() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + + super().setUp() + Environment._is_initialized = True + Environment._env_prefix = "APP_" + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + @patch('os.path.dirname') + @patch('os.makedirs') + def test_get_str_as_file_creates_directory_for_file(self, mock_makedirs, mock_dirname, mock_get_str): + """ + Тест проверяет создание директории для файла + """ + + # Arrange + mock_get_str.return_value = "/logs/app.log" + mock_dirname.return_value = "/logs" + + # Act + result = Environment.get_str_as_file("LOG_FILE") + + # Assert + self.assertEqual(result, "/logs/app.log") + mock_dirname.assert_called_once_with("/logs/app.log") + mock_makedirs.assert_called_once_with("/logs", exist_ok=True) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + @patch('os.path.dirname') + @patch('os.makedirs') + def test_get_str_as_file_with_default_value(self, mock_makedirs, mock_dirname, mock_get_str): + """ + Тест проверяет создание директории для файла со значением по умолчанию + """ + + # Arrange + mock_get_str.return_value = "/default/app.log" + mock_dirname.return_value = "/default" + + # Act + result = Environment.get_str_as_file("LOG_FILE", "/default/app.log") + + # Assert + self.assertEqual(result, "/default/app.log") + mock_makedirs.assert_called_once_with("/default", exist_ok=True) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + @patch('os.path.dirname') + @patch('os.makedirs') + def test_get_str_as_file_handles_filename_only(self, mock_makedirs, mock_dirname, mock_get_str): + """ + Тест проверяет создание директории для файла без пути + """ + + # Arrange + mock_get_str.return_value = "app.log" + mock_dirname.return_value = "" + + # Act + result = Environment.get_str_as_file("LOG_FILE") + + # Assert + self.assertEqual(result, "app.log") + mock_dirname.assert_called_once_with("app.log") + # Для пустой директории makedirs должен обработать корректно + mock_makedirs.assert_called_once_with("", exist_ok=True) + diff --git a/net/xeaf/rack/tests/utils/settings/environment_get_str_as_list_tests.py b/net/xeaf/rack/tests/utils/settings/environment_get_str_as_list_tests.py new file mode 100644 index 0000000..9f55a6b --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_get_str_as_list_tests.py @@ -0,0 +1,133 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentGetStrAsListTests +""" + +from unittest.mock import patch + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentGetStrAsListTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.get_str_as_list() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + super().setUp() + Environment._is_initialized = True + Environment._env_prefix = "APP_" + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_returns_list_from_comma_separated(self, mock_get_str): + """ + Тест проверяет преобразование строки с разделителями-запятыми в список + """ + + # Arrange + mock_get_str.return_value = "item1,item2,item3" + + # Act + result = Environment.get_str_as_list("ITEMS") + + # Assert + self.assertEqual(result, ["item1", "item2", "item3"]) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_strips_spaces(self, mock_get_str): + """ + Тест проверяет удаление пробелов вокруг элементов + """ + + # Arrange + mock_get_str.return_value = " item1 , item2 , item3 " + + # Act + result = Environment.get_str_as_list("ITEMS") + + # Assert + self.assertEqual(result, ["item1", "item2", "item3"]) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_returns_empty_list_for_empty_string(self, mock_get_str): + """ + Тест проверяет возврат пустого списка для пустой строки + """ + + # Arrange + mock_get_str.return_value = "" + + # Act + result = Environment.get_str_as_list("ITEMS") + + # Assert + self.assertEqual(result, []) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_filters_empty_items(self, mock_get_str): + """ + Тест проверяет фильтрацию пустых элементов + """ + + # Arrange + mock_get_str.return_value = "item1,,item2,,,item3" + + # Act + result = Environment.get_str_as_list("ITEMS") + + # Assert + self.assertEqual(result, ["item1", "item2", "item3"]) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_returns_single_item(self, mock_get_str): + """ + Тест проверяет возврат списка с одним элементом + """ + + # Arrange + mock_get_str.return_value = "single_item" + + # Act + result = Environment.get_str_as_list("ITEMS") + + # Assert + self.assertEqual(result, ["single_item"]) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_with_default_value(self, mock_get_str): + """ + Тест проверяет обработку списка со значением по умолчанию + """ + + # Arrange + mock_get_str.return_value = "default1,default2" + + # Act + result = Environment.get_str_as_list("ITEMS", "default1,default2") + + # Assert + self.assertEqual(result, ["default1", "default2"]) + + @patch('net.xeaf.rack.utils.settings.Environment.get_str') + def test_get_str_as_list_handles_multiple_spaces_between_items(self, mock_get_str): + """ + Тест проверяет обработку множественных пробелов между элементами + """ + + # Arrange + mock_get_str.return_value = "item1 , item2 , item3" + + # Act + result = Environment.get_str_as_list("ITEMS") + + # Assert + self.assertEqual(result, ["item1", "item2", "item3"]) diff --git a/net/xeaf/rack/tests/utils/settings/environment_get_str_tests.py b/net/xeaf/rack/tests/utils/settings/environment_get_str_tests.py new file mode 100644 index 0000000..a79f2b6 --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_get_str_tests.py @@ -0,0 +1,118 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentGetStrTests +""" + +from unittest.mock import Mock +from unittest.mock import patch + +from environ import Env + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentGetStrTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.get_str() + """ + + def setUp(self): + """ + Подготовка тестовой среды + """ + super().setUp() + Environment._is_initialized = True + Environment._env_prefix = "APP_" + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_str_returns_string_value(self, mock_env): + """ + Тест проверяет получение строкового значения переменной окружения + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.str.return_value = "test_value" + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_str("TEST_VAR") + + # Assert + self.assertEqual(result, "test_value") + mock_env_instance.str.assert_called_once_with("APP_TEST_VAR", Env.NOTSET) + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_str_with_default_value(self, mock_env): + """ + Тест проверяет получение строкового значения с указанием значения по умолчанию + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.str.return_value = "default_value" + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_str("TEST_VAR", "default_value") + + # Assert + self.assertEqual(result, "default_value") + mock_env_instance.str.assert_called_once_with("APP_TEST_VAR", "default_value") + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_str_with_env_notset(self, mock_env): + """ + Тест проверяет получение строкового значения с Env.NOTSET + """ + + # Arrange + mock_env_instance = Mock() + mock_env_instance.str.return_value = "value" + mock_env.return_value = mock_env_instance + + # Act + result = Environment.get_str("TEST_VAR", Env.NOTSET) + + # Assert + self.assertEqual(result, "value") + mock_env_instance.str.assert_called_once_with("APP_TEST_VAR", Env.NOTSET) + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_str_adds_prefix_to_name(self, mock_env): + """ + Тест проверяет добавление префикса к имени переменной + """ + + # Arrange + mock_env_instance = Mock() + mock_env.return_value = mock_env_instance + + # Act + Environment.get_str("API_KEY") + + # Assert + mock_env_instance.str.assert_called_once_with("APP_API_KEY", Env.NOTSET) + + @patch('net.xeaf.rack.utils.settings.Environment.env') + def test_get_str_with_empty_prefix(self, mock_env): + """ + Тест проверяет получение значения без префикса + """ + + # Arrange + Environment._env_prefix = "" + mock_env_instance = Mock() + mock_env.return_value = mock_env_instance + + # Act + Environment.get_str("TEST_VAR") + + # Assert + mock_env_instance.str.assert_called_once_with("TEST_VAR", Env.NOTSET) diff --git a/net/xeaf/rack/tests/utils/settings/environment_initialize_tests.py b/net/xeaf/rack/tests/utils/settings/environment_initialize_tests.py new file mode 100644 index 0000000..84b3621 --- /dev/null +++ b/net/xeaf/rack/tests/utils/settings/environment_initialize_tests.py @@ -0,0 +1,117 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса EnvironmentInitializeTests +""" + +from unittest.mock import patch + +from environ import Env + +from net.xeaf.rack.core.testing import CoreSimpleTestCase +from net.xeaf.rack.utils.settings import Environment + + +class EnvironmentInitializeTests(CoreSimpleTestCase): + """ + Тестовый класс для Environment.initialize() + """ + + def setUp(self): + """ + Подготовка тестовой среды: сброс состояния Environment + """ + + super().setUp() + # Сбрасываем состояние класса перед каждым тестом + Environment._is_initialized = False + Environment._base_dir = "" + Environment._env = Env() + Environment._env_prefix = "" + + @patch('environ.Env') + @patch('os.path.join') + def test_initialize_sets_base_dir_and_env(self, mock_join, mock_env_class): + """ + Тест проверяет, что initialize корректно устанавливает базовую директорию и объект Env + """ + + # Arrange + base_dir = "/app/project" + env_prefix = "APP_" + mock_join.return_value = "/app/project/.env" + + # Act + Environment.initialize(base_dir, env_prefix) + + # Assert + self.assertEqual(Environment._base_dir, base_dir) + self.assertEqual(Environment._env_prefix, env_prefix) + self.assertTrue(Environment._is_initialized) + mock_env_class.read_env.assert_called_once_with("/app/project/.env") + + @patch('environ.Env') + @patch('os.path.join') + def test_initialize_with_empty_prefix(self, mock_join, mock_env_class): + """ + Тест проверяет инициализацию с пустым префиксом + """ + + # Arrange + base_dir = "/app/project" + env_prefix = "" + mock_join.return_value = "/app/project/.env" + + # Act + Environment.initialize(base_dir, env_prefix) + + # Assert + self.assertEqual(Environment._env_prefix, "") + mock_env_class.read_env.assert_called_once_with("/app/project/.env") + + @patch('environ.Env') + @patch('os.path.join') + def test_initialize_called_multiple_times(self, mock_join, mock_env_class): + """ + Тест проверяет повторную инициализацию + """ + + # Arrange + mock_join.return_value = "/app/project/.env" + + # Act + Environment.initialize("/app/project", "PREFIX_") + first_env = Environment._env + + # Меняем поведение join для второго вызова, если это необходимо по вашей логике + mock_join.return_value = "/app/other/.env" + Environment.initialize("/app/other", "OTHER_") + second_env = Environment._env + + # Assert + self.assertEqual(first_env, second_env) # Оба должны указывать на наш mock_env_class + self.assertEqual(Environment._base_dir, "/app/other") + self.assertEqual(Environment._env_prefix, "OTHER_") + self.assertEqual(mock_env_class.read_env.call_count, 2) + + @patch('environ.Env') + @patch('os.path.join') + def test_initialize_reads_dot_env_file(self, mock_join, mock_env_class): + """ + Тест проверяет, что initialize читает файл .env через класс-метод + """ + + # Arrange + base_dir = "/app/project" + expected_env_path = "/app/project/.env" + mock_join.return_value = expected_env_path + + # Act + Environment.initialize(base_dir) + + # Assert + mock_env_class.read_env.assert_called_once_with(expected_env_path) diff --git a/net/xeaf/rack/utils/settings/environment.py b/net/xeaf/rack/utils/settings/environment.py index 723af3c..9225d53 100644 --- a/net/xeaf/rack/utils/settings/environment.py +++ b/net/xeaf/rack/utils/settings/environment.py @@ -5,7 +5,7 @@ # Все права защищены. Лицензия: MIT """ -Описание класса EnvUtils +Описание класса Environment """ import os @@ -21,16 +21,16 @@ class Environment: Реализация доступа к параметрам и переменным окружения """ - __base_dir: str + _base_dir: str """ Путь к корневой директории приложения """ - __is_initialized: bool + _is_initialized: bool """ Признак инициализации конфигурации """ - __env: Env + _env: Env """ Хранилище переменных окружения """ - __env_prefix: str + _env_prefix: str """ Префикс переменных окружения """ @classmethod @@ -42,11 +42,12 @@ class Environment: :param env_prefix: Префикс переменных окружения """ - cls.__base_dir = base_dir - cls.__env = environ.Env() - cls.__env_prefix = env_prefix - environ.Env.read_env(os.path.join(cls.__base_dir, '.env')) - cls.__is_initialized = True + cls._base_dir = base_dir + cls._env = environ.Env() + cls._env_prefix = env_prefix + env_path = os.path.join(cls._base_dir, '.env') + environ.Env.read_env(env_path) + cls._is_initialized = True @classmethod def env(cls) -> Env: @@ -57,7 +58,7 @@ class Environment: """ cls._check_initialization() - return cls.__env + return cls._env @classmethod def get_str(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str: @@ -158,7 +159,7 @@ class Environment: :return: Имя переменной окружения """ - return f"{cls.__env_prefix}{name}" + return f"{cls._env_prefix}{name}" @classmethod def _check_initialization(cls) -> None: @@ -166,5 +167,5 @@ class Environment: Проверяет завершение инициализации """ - if not cls.__is_initialized: + if not cls._is_initialized: raise ImproperlyConfigured(f"Class {__class__.__name__} is not initialized.")