diff --git a/.vscode/settings.json b/.vscode/settings.json index e64211a..97d59ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,10 @@ "source.organizeImports": "explicit" }, "editor.defaultFormatter": "charliermarsh.ruff" - } + }, + "python.testing.pytestArgs": [ + "src/" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } \ No newline at end of file diff --git a/src/config.py b/src/config.py index 02ec31f..00614e5 100644 --- a/src/config.py +++ b/src/config.py @@ -1,7 +1,45 @@ -import os +from __future__ import annotations -from dotenv import load_dotenv +import os +from pathlib import Path +from typing import TYPE_CHECKING, ClassVar + +from dotenv import dotenv_values, load_dotenv, set_key, unset_key + +if TYPE_CHECKING: + from collections import OrderedDict + +config_path = Path(__file__).parent.resolve() +DOT_ENV_PATH = Path(config_path, ".env") +DOT_ENV_PATH.touch(mode=0o600, exist_ok=True) load_dotenv() +VERSION = "1.5" NOTION_TOKEN = os.getenv("NOTION_TOKEN") + + +class Config: + env_dict: ClassVar[OrderedDict[str, str]] = {} + dot_env_path = DOT_ENV_PATH + + @staticmethod + def init(dotenv_path: str = DOT_ENV_PATH) -> None: + Config.dot_env_path = dotenv_path + Config.env_dict = dotenv_values(dotenv_path=dotenv_path) + + @staticmethod + def get_env(key: str) -> str | None: + if key in Config.env_dict: + return Config.env_dict[key] + return None + + @staticmethod + def update_env(key: str, value: str) -> None: + set_key(Config.dot_env_path, key, value) + Config.env_dict = dotenv_values(dotenv_path=Config.dot_env_path) + + @staticmethod + def remove_env(key: str) -> None: + unset_key(Config.dot_env_path, key) + Config.env_dict = dotenv_values(dotenv_path=Config.dot_env_path) diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/test_config.py b/src/tests/test_config.py new file mode 100644 index 0000000..26ccde5 --- /dev/null +++ b/src/tests/test_config.py @@ -0,0 +1,70 @@ +from collections import OrderedDict +from pathlib import Path + +import pytest +from dotenv import dotenv_values, set_key + +from src.config import Config + +CONFIG_PATH = Path(__file__).parent.resolve() +TEST_DOT_ENV_PATH = Path(CONFIG_PATH, ".env_test") + +EXPECTED_ENV_DICT: OrderedDict[str, str] = OrderedDict( + { + "KEY_1": "VALUE_1", + "KEY_2": "VALUE_2", + "NOTION_TOKEN": "1234454_234324", + }, +) + + +@pytest.fixture() +def _prepare_test_dot_env() -> any: + TEST_DOT_ENV_PATH.touch(mode=0o600, exist_ok=True) + for key, value in EXPECTED_ENV_DICT.items(): + set_key(TEST_DOT_ENV_PATH, key, value) + yield + TEST_DOT_ENV_PATH.unlink() + + +@pytest.fixture() +def _load_test_dot_env(_prepare_test_dot_env: any) -> None: + Config.init(dotenv_path=TEST_DOT_ENV_PATH) + + +@pytest.mark.usefixtures("_prepare_test_dot_env") +def test_init_config() -> None: + assert Config.env_dict == {} + Config.init(dotenv_path=TEST_DOT_ENV_PATH) + assert Config.env_dict == EXPECTED_ENV_DICT + dict_from_file = dotenv_values(dotenv_path=TEST_DOT_ENV_PATH) + assert dict_from_file == EXPECTED_ENV_DICT + + +@pytest.mark.usefixtures("_load_test_dot_env") +def test_get_config() -> None: + assert Config.get_env("NON_EXISTING_KEY") is None + key_1 = "KEY_1" + assert Config.get_env(key_1) == EXPECTED_ENV_DICT[key_1] + + +@pytest.mark.usefixtures("_load_test_dot_env") +def test_update_config() -> None: + key = "KEY_1" + value = EXPECTED_ENV_DICT[key] + new_value = "NEW_VALUE" + assert Config.get_env(key) == value + Config.update_env(key, new_value) + assert Config.get_env(key) == new_value + dict_from_file = dotenv_values(dotenv_path=TEST_DOT_ENV_PATH) + assert dict_from_file[key] == new_value + + +@pytest.mark.usefixtures("_load_test_dot_env") +def test_remove_config() -> None: + key = "KEY_1" + assert Config.get_env(key) == EXPECTED_ENV_DICT[key] + Config.remove_env(key) + assert Config.get_env(key) is None + dict_from_file = dotenv_values(dotenv_path=TEST_DOT_ENV_PATH) + assert key not in dict_from_file