sintonia/shared/tests/config/env_test.py
jo bcd877266f feat(shared): load env config using jsonschema
The env loader is now capable of loading lists of objects, union types
or list of union types from the env variables.
They are some limitations: for example it doesn't support unions of
different shapes `list | dict` or `str | dict`.
2022-07-31 21:43:34 +02:00

189 lines
5.5 KiB
Python

# pylint: disable=protected-access
from os import environ
from typing import List, Union
from unittest import mock
import pytest
from pydantic import BaseModel
from libretime_shared.config import BaseConfig
from libretime_shared.config._env import EnvLoader
ENV_SCHEMA_OBJ_WITH_STR = {
"type": "object",
"properties": {"a_str": {"type": "string"}},
}
@pytest.mark.parametrize(
"env_parent, env, schema, expected",
[
(
"PRE",
{"PRE_A_STR": "found"},
{"a_str": {"type": "string"}},
{"a_str": "found"},
),
(
"PRE",
{"PRE_OBJ_A_STR": "found"},
{"obj": ENV_SCHEMA_OBJ_WITH_STR},
{"obj": {"a_str": "found"}},
),
(
"PRE",
{"PRE_ARR1": "one, two"},
{"arr1": {"type": "array", "items": {"type": "string"}}},
{"arr1": ["one", "two"]},
),
(
"PRE",
{
"PRE_ARR2_0_A_STR": "one",
"PRE_ARR2_1_A_STR": "two",
"PRE_ARR2_3_A_STR": "ten",
},
{"arr2": {"type": "array", "items": ENV_SCHEMA_OBJ_WITH_STR}},
{
"arr2": [
{"a_str": "one"},
{"a_str": "two"},
None,
{"a_str": "ten"},
]
},
),
],
)
def test_env_config_loader_get_object(
env_parent,
env,
schema,
expected,
):
with mock.patch.dict(environ, env):
loader = EnvLoader(schema={}, env_prefix="PRE")
result = loader._get_object(env_parent, {"properties": schema})
assert result == expected
class FirstChildConfig(BaseModel):
a_child_str: str
class SecondChildConfig(BaseModel):
a_child_str: str
a_child_int: int
# pylint: disable=too-few-public-methods
class FixtureConfig(BaseConfig):
a_str: str
a_list_of_str: List[str]
a_obj: FirstChildConfig
a_obj_with_default: FirstChildConfig = FirstChildConfig(a_child_str="default")
a_list_of_obj: List[FirstChildConfig]
a_union_str_or_int: Union[str, int]
a_union_obj: Union[FirstChildConfig, SecondChildConfig]
a_list_of_union_str_or_int: List[Union[str, int]]
a_list_of_union_obj: List[Union[FirstChildConfig, SecondChildConfig]]
ENV_SCHEMA = FixtureConfig.schema()
@pytest.mark.parametrize(
"env_name, env, schema, expected",
[
(
"PRE_A_STR",
{"PRE_A_STR": "found"},
ENV_SCHEMA["properties"]["a_str"],
"found",
),
(
"PRE_A_LIST_OF_STR",
{"PRE_A_LIST_OF_STR": "one, two"},
ENV_SCHEMA["properties"]["a_list_of_str"],
["one", "two"],
),
(
"PRE_A_OBJ",
{"PRE_A_OBJ_A_CHILD_STR": "found"},
ENV_SCHEMA["properties"]["a_obj"],
{"a_child_str": "found"},
),
],
)
def test_env_config_loader_get(
env_name,
env,
schema,
expected,
):
with mock.patch.dict(environ, env):
loader = EnvLoader(schema=ENV_SCHEMA, env_prefix="PRE")
result = loader._get(env_name, schema)
assert result == expected
def test_env_config_loader_load_empty():
with mock.patch.dict(environ, {}):
loader = EnvLoader(schema=ENV_SCHEMA, env_prefix="PRE")
result = loader.load()
assert not result
def test_env_config_loader_load():
with mock.patch.dict(
environ,
{
"PRE_A_STR": "found",
"PRE_A_LIST_OF_STR": "one, two",
"PRE_A_OBJ": "invalid",
"PRE_A_OBJ_A_CHILD_STR": "found",
"PRE_A_OBJ_WITH_DEFAULT_A_CHILD_STR": "found",
"PRE_A_LIST_OF_OBJ": "invalid",
"PRE_A_LIST_OF_OBJ_0_A_CHILD_STR": "found",
"PRE_A_LIST_OF_OBJ_1_A_CHILD_STR": "found",
"PRE_A_LIST_OF_OBJ_3_A_CHILD_STR": "found",
"PRE_A_LIST_OF_OBJ_INVALID": "invalid",
"PRE_A_UNION_STR_OR_INT": "found",
"PRE_A_UNION_OBJ_A_CHILD_STR": "found",
"PRE_A_UNION_OBJ_A_CHILD_INT": "found",
"PRE_A_LIST_OF_UNION_STR_OR_INT": "one, two, 3",
"PRE_A_LIST_OF_UNION_STR_OR_INT_3": "4",
"PRE_A_LIST_OF_UNION_OBJ": "invalid",
"PRE_A_LIST_OF_UNION_OBJ_0_A_CHILD_STR": "found",
"PRE_A_LIST_OF_UNION_OBJ_1_A_CHILD_STR": "found",
"PRE_A_LIST_OF_UNION_OBJ_1_A_CHILD_INT": "found",
"PRE_A_LIST_OF_UNION_OBJ_3_A_CHILD_INT": "found",
"PRE_A_LIST_OF_UNION_OBJ_INVALID": "invalid",
},
):
loader = EnvLoader(schema=ENV_SCHEMA, env_prefix="PRE")
result = loader.load()
assert result == {
"a_str": "found",
"a_list_of_str": ["one", "two"],
"a_obj": {"a_child_str": "found"},
"a_obj_with_default": {"a_child_str": "found"},
"a_list_of_obj": [
{"a_child_str": "found"},
{"a_child_str": "found"},
None,
{"a_child_str": "found"},
],
"a_union_str_or_int": "found",
"a_union_obj": {
"a_child_str": "found",
"a_child_int": "found",
},
"a_list_of_union_str_or_int": ["one", "two", "3", "4"],
"a_list_of_union_obj": [
{"a_child_str": "found"},
{"a_child_str": "found", "a_child_int": "found"},
None,
{"a_child_int": "found"},
],
}